编译原理实验三LL(1)语法分析器的构造教材
编译原理实验LL1分析法
编译原理程序设计实验报告——表达式语法分析器的设计实现班级:计算机1306班姓名:王利达学号:20133959 实验目标:使用LL(1)分析法构造表达式语法分析器程序,判别算术表达式,给出判别结果。
实验内容:一、概要设计1.算术表达式文法:E→ T | Eω0TT→ F | Tω1FF→ i | ( E )其中ω0:+ -ω1:* /i:数字或常数文法变换:E → T MM →ω0 T M |εT → F | NN →ω1 F N |εF → i | ( E )其中ω0:+ -ω1:* /i:数字或常数2.LL(1)分析表表1.LL(1)分析表i + - * / ( ) #E MT,p MT,pM MT,n MT,n ε,p ε,pT NF,p NF,pN NF,n NF,n ε,p ε,pF ε,n )E,n) ε,n# OK二、数据结构1.输入表达式定义char型数组expstr为存放输入表达式的数组,char expstr[100];2.分析栈定义一个栈来进行LL(1)分析。
栈中有bottom、top、stacksize 等元素,用于程序调用栈和对栈操作。
typedef struct //定义语法的栈{SElemType *bottom;//底SElemType *top;//顶int stacksize;}SqStack;(包括:概要设计、数据结构、流程图、关键函数等有选择填写)源程序代码:(加入注释)#include<iostream>#include<stdio.h>#include<cstdlib>using namespace std;#define STACKSIZE 30 //栈大小#define STACKINCREMENT 10 //栈增量#define OK 1#define Error 0#define OVERFLOW -1typedef char SElemType;typedef int Status;int i=0;int count1=0;int count2=0; //计数终结符的个数char expstr[100];typedef struct //定义语法的栈{SElemType *bottom;//底SElemType *top;//顶int stacksize;}SqStack;Status InitStack(SqStack &S) //初始化栈{S.bottom=(SElemType*)malloc(STACKSIZE*sizeof(SElemType));if(!S.bottom)exit(OVERFLOW);S.top=S.bottom;S.stacksize=STACKSIZE;return OK;}Status PUSH(SqStack &S,SElemType e){if(S.top-S.bottom>=S.stacksize){S.bottom=(SElemType*)realloc(S.bottom,(S.stacksize+STACKINCREMENT)*sizeof(SElemType ));if(!S.bottom)exit(OVERFLOW);S.top=S.bottom+S.stacksize;S.stacksize+=STACKINCREMENT;}*(S.top)=e;(S.top)++;return OK;}Status POP(SqStack &S,SElemType &e){if(S.top==S.bottom)return Error;S.top--;e=*(S.top);return OK;}void wrong()//调用此函数,表示出错{cout<<"Wrong!"<<endl;getchar();exit(0);}bool ch_judge(char s[],int n) //字符是否合法{if((s[n]=='(')||(s[n]==')')||(s[n]>='0'&&s[n]<='9')||(s[n]>='a'&&s[n]<='z')||(s[n]=='+')||(s[n]=='-')||(s[ n]=='*')||(s[n]=='/')||(s[n]=='#'))return 1;elsereturn 0;}bool ter_judge(char c) //终结符集合与非终结符集合{if((c=='(')||(c==')')||(c>='0'&&c<='9')||(c>='a'&&c<='z')||(c=='+')||(c=='-')||(c=='*')||(c=='/')) return 1; //终结符返回1else //if(c=='E'||c=='E1'||c=='T'||c=='T1'||c=='F')return 0; //非终结符或#,返回0}bool num_letter(char s[],int i) //判断当前字符是数字还是字母{if((s[i]>='0'&&s[i]<='9')||(s[i]>='a'&&s[i]<='z')){i++;while((s[i]>='0'&&s[i]<='9')||(s[i]>='a'&&s[i]<='z')){i++;count1++;}while(count1!=0){i--;count1--;}i--;return 1; //是字母或数字返回1}elsereturn 0; //不是字母或数字返回0}void LL1(SqStack &S,char s[],int i)//LL1文法分析函数{SElemType e;PUSH(S,'#');PUSH(S,'E');while(s[i]!='#'){if(ch_judge(s,i)){POP(S,e);if(!(ter_judge(e))) //是非终结符{if(e=='#'&&s[i]=='#')break; //表达式正确else if(e=='#'&&s[i]!='#')wrong();else if(e!='#') //分析表匹配{switch(e){case 'E':if(num_letter(s,i)||s[i]=='('){e='M'; // E' MPUSH(S,e);e='T';PUSH(S,e);break;}elsewrong();case 'M':if(s[i]=='+'||s[i]=='-'){e='M';PUSH(S,e);e='T';PUSH(S,e);e=s[i];PUSH(S,e);break;}else if(s[i]==')'||s[i]=='#'){break;}elsewrong();case 'T':if(num_letter(s,i)||s[i]=='('){e='N'; //T' NPUSH(S,e);e='F';PUSH(S,e);break;}elsewrong();case 'N':if(s[i]=='+'||s[i]=='-'||s[i]==')'||s[i]=='#'){break;}else if(s[i]=='*'||s[i]=='/'){e='N';PUSH(S,e);e='F';PUSH(S,e);e=s[i];PUSH(S,e);break;}elsewrong();case 'F':if(num_letter(s,i)){while((s[i]>='0'&&s[i]<='9')||(s[i]>='a'&&s[i]<='z')) //将连续的终结符压栈{e=s[i];PUSH(S,e);i++;count2++; //给连续终结符计数}i--; //使s[i]为连续终结符即一个整数的最后一字break;}else if(s[i]=='('){e=')';PUSH(S,e);e='E';PUSH(S,e);e='(';PUSH(S,e);break;}elsewrong();default:wrong();}}}else{if((s[i]>='0'&&s[i]<='9')||(s[i]>='a'&&s[i]<='z'))//如果是数字或字母则依次计数{while(ter_judge(e)){if(e==s[i]){i--;POP(S,e);}elsewrong();}while(count2!=0){i++;count2--;}PUSH(S,e);i++;}else //如果是+-*/则直接比较{if(e==s[i])i++;elsewrong();}}}elsewrong();}}int main(){SqStack S;InitStack(S);cout<<"LL(1)\nPlease enter the expression and end with the \"#\":"<<endl;cin>>expstr;LL1(S,expstr,i);cout<<"Right! "<<endl;getchar();return 0;}程序运行结果:(截屏)图1.正确的算术表达式(((a+b)*(c/B)))的判断图2.错误的算术表达式a/b++的判断思考问题回答:(如果有)LL(1)分析法和递归下降子程序法在语法分析中同属于自顶向下分析法,LL(1)分析法相对于递归下降子程序法的优势是:LL(1)分析法消除了文法的左递归性,而且克服了回溯,使程序运行的效率大大提升。
编译原理上机实验-LL(1)语法分析-C#
编译原理上机实验报告小组成员:王金名、周攀、汪国辉、澎湃、王帅、齐娟娟、刘鸳鸳一、实验目的:了解LL(1)文法分析的基本原理;提高上机实践能力;增强团队协作能力。
二、实验内容:通过LL1文法分析表分析任意一个符号串是否为某文法的句子;显示具体分析过程;打开、新建、保存分析表;保存分析结果三、实验原理:1.C#字符串处理及数组处理,这是本实验最强有力的工具;2.LL(1)文法分析的基本原理,详见教材P80 LL(1)分析器的总控算法;3.C#文件操作,C#常用控件使用。
四、实验步骤:1.构造应用程序框架,利用内置分析表实现分析符号串的最基础功能(1)使用Visual Studio 2005 新建C#语言环境的windows应用程序LL1GAnalysis;(2)将窗体的名称改成From_Main,相应的代码名称会随之更改;(3)添加texbox控件ID为textBox_input,添加listView控件,ID为listView_Result;(4)public partial class Form_Main : Form里面编写相应代码://全局变量const int Max = 100;public string[,] staticmTable ={{"","i","+","*","(",")","#"},{"S","<error>","<error>","<error>","S::=A","S::=A","<error>"},{"A","<error>","<error>","<error>","A::=BA\'","A::=BA\'","<error>"},{"A\'","A\'::=iBA\'","<error>","A\'::=ε","<error>","<error>","A\'::=ε"},{"B","<error>","<error>","<error>","B::=CB\'","B::=CB\'","<error>"},{"B\'","B\'::=ε","B\'::=+CB\'","B\'::=ε","<error>","<error>","B\'::=ε"},{"C","<error>","<error>","<error>","C::=(","C::=)A*","<error>"} };string[] VN = new string[Max]; int VNLength;string[] VT = new string[Max]; int VTLength;//以下是分析过程中要用到的公共函数public void addTolistView_Result(string step, string stack, string input, stringproduction)//分析步骤及结果显示(向listView中添加条目,并保存到string类型变量(analysisResult)作最终保存分析结果时使用{string strResultbuf = "";strResultbuf += step.PadRight(20, ' ');strResultbuf += stack.PadRight(20, ' ');strResultbuf += input.PadRight(20, ' ');strResultbuf += production.PadRight(20, ' ') + "\r\n";analysisResult += strResultbuf;ListViewItem li = new ListViewItem();li.Text = step;ListViewItem.ListViewSubItem ls = new ListViewItem.ListViewSubItem();ls.Text = stack;li.SubItems.Add(ls);ls = new ListViewItem.ListViewSubItem();ls.Text = input;li.SubItems.Add(ls);ls = new ListViewItem.ListViewSubItem();ls.Text = production;li.SubItems.Add(ls);listView_Result.Items.Add(li);}public void GetVN(string[,] table)//从分析表中获取非终结符{int i;for ( i = 1; i < table.GetLength(0);i++ ){VN[i-1] = table[i,0];}VNLength = i;}public void GetVT(string[,] table)//从分析表中获取终结符{int i;for (i = 1; i < table.GetLength(1); i++){VT[i-1] = table[0,i];}VTLength = i;}public int isVT(string str)//判断str是不是VT中的符号{int mark = 0;for (int i = 0; i < VTLength; i++){if (VT[i] == str){mark = 1;}}return mark;}public int isVN(string str)//判断str是不是VN中的符号{int mark = 0;for (int i = 0; i < VNLength; i++){if (VN[i] == str){mark = 1;}}return mark;}public string outStack(string[] Stack, int top)//栈内符号合并输出的时候用 {string str = "";for (int i = 0; i <= top; i++){str += Stack[i];}return str;}public void removeAllItems(ListView list)//清空listview Items{int itemcount = list.Items.Count;for (int i = itemcount; i > 0 ;i-- ){list.Items.RemoveAt(0);}}public string matchInTable(string[,] mt,string stacktop, string nowstr)//查表栈顶与ch 交叉处的标志并返回{int i,j;for (i = 0; i < mt.GetLength(0); i++ ){if (mt[i,0] == stacktop){break;}}for (j = 0; j < mt.GetLength(1);j++ ){if (mt[0,j] == nowstr){break;}}if (i < mt.GetLength(0)&&j<mt.GetLongLength(1)){return mt[i,j];}else{return"error! ";}}//以下是分析过程public void Start_Analysis(string[,] mTable)//开始分析并显示分析过程{tabControl_mTable.SelectTab(tabPage_Show);ShowmTable(mTable);removeAllItems(listView_Result);listView_Result.BeginUpdate();int top = -1; int step = 0;string[] Stack = new string[Max];string str = textBox_input.Text.Trim();//初始化GetVN(mTable); GetVT(mTable);top++; Stack[top] = "#";top++; Stack[top] = VN[0];str += "#";//分析while (true){if (isVT(Stack[top]) == 1)//Stack[top]是终结符,则比较栈顶符与当前符号{if (Stack[top] == str[0].ToString())//匹配当前符号{if (Stack[top] == "#")//ok{step++;addTolistView_Result(step.ToString(), outStack(Stack, top), str, "OK!");step = 0;break;}else//同时退栈{step++;addTolistView_Result(step.ToString(), outStack(Stack, top), str, ""); top--;str = str.Remove(0, 1);}}else//错误{step++;addTolistView_Result(step.ToString(), outStack(Stack, top), str,"Error!");break;}}else if (isVN(Stack[top]) == 1)//Stack[top]是非终结符,则查表{string production = matchInTable(mTable, Stack[top], str[0].ToString());if (production != "<error>"){step++;addTolistView_Result(step.ToString(), outStack(Stack, top), str, production);string probuf = "";if (production[1] == '\''){probuf = production.Remove(0, 5);}else{probuf = production.Remove(0, 4);}char[] chbuf = probuf.ToCharArray();int i = chbuf.Length - 1;string strbuf = "";Stack[top] = null;top--;while (i >= 0){if (chbuf[i] != 'ε'){if (chbuf[i] != '\''){top++;Stack[top] = strbuf.Insert(0, chbuf[i].ToString());strbuf = "";}else if (chbuf[i] == '\''){strbuf += strbuf.Insert(0, chbuf[i].ToString());}}else { break; }i--;}}else//错误production.Length != 0不在分析表中{step++;addTolistView_Result(step.ToString(), outStack(Stack, top), str, "Error!");break;}}else//错误非法字符{step++;addTolistView_Result(step.ToString(), "错误", str, "非法字符:" +str[0].ToString());break;}}//分析结束listView_Result.EndUpdate();}private void button_Start_Click(object sender, EventArgs e)//菜单及按钮开始分析(菜单项及开始按钮公用函数){string[,] mTable;if (radioButton_Staticmt.Checked){mTable = staticmTable;ShowmTable(mTable);Start_Analysis(mTable);}else if (radioButton_Createmt.Checked){if (created == 1){mTable = creamTable;ShowmTable(mTable);Start_Analysis(mTable);}else{MessageBox.Show("你还没有创建分析表,请先创建!", "错误提示",MessageBoxButtons.OK, MessageBoxIcon.Exclamation);}}else if (radioButton_Openmt.Checked){if (opened == 1){mTable = openmTable;ShowmTable(mTable);Start_Analysis(mTable);}else{MessageBox.Show("你还没有打开分析表,请先打开或创建!", "错误提示",MessageBoxButtons.OK, MessageBoxIcon.Exclamation);}}}2.实现显示分析表及新建分析表并利用该表分析句子的功能(1)添加tabControl控件ID为tabControl_mTable建立3个页面tabPage_Show显示当前分析表、tabPage_Edit新建分析表、tabPage_Open打开分析表(2)tabPage_Show中添加listView控件ID为listView_mtableshow用于显示分析表(3)tabPage_Edit中添加两个Button 控件ID分别为button_StartAdd开始添加、button_FilishAdd 完成添加;两个textBox控件ID分别为textBox_VT 、textBox_VN、分别用于获取要添加的终结符及非终结符个数;一个tableLayoutPanel控件ID为tableLayoutPanel_mTable用于根据用户输入的VT及VN的个数建立输入表(4)编辑相应代码:private void button_StartAdd_Click(object sender, EventArgs e)//新建分析表并开始输入{try{int conwidth,conheight,col, row;TextBox txb;tableLayoutPanel_mTable.Controls.Clear();conwidth = 50; conheight = 20;col = Convert.ToInt32(textBox_VT.Text)+1;row = Convert.ToInt32(textBox_VN.Text)+1;tableLayoutPanel_mTable.ColumnCount =col;tableLayoutPanel_mTable.RowCount = row;for (int i = 0; i < col;i++ ){for (int j = 0; j < row;j++ ){if (i == 0&&j==0){Label lb = new Label();lb.Text = "VN\\VT";lb.Width = conwidth;lb.ForeColor = Color.Red;tableLayoutPanel_mTable.Controls.Add(lb,i,j);}else{txb = new TextBox();txb.Width = conwidth;txb.Height = conheight;tableLayoutPanel_mTable.Controls.Add(txb,i,j);}}}}catch{MessageBox.Show("终结符或非终结符格式不对!\n请输入数字!","错误提示",MessageBoxButtons.OK, MessageBoxIcon.Exclamation);}}private void button_FilishAdd_Click(object sender, EventArgs e)//完成添加,更新分析表 {int col = tableLayoutPanel_mTable.ColumnCount;int row = tableLayoutPanel_mTable.RowCount;if (col > 1&&row>1){creamTable = new string[row, col];for (int i = 0; i < row; i++){for (int j = 0; j < row; j++){if (i == 0 && j == 0){creamTable[i, j] = "";}else{creamTable[i, j] =((TextBox)tableLayoutPanel_mTable.GetControlFromPosition(j, i)).Text.Trim();if (creamTable[i,j].Length == 0){creamTable[i, j] = "<error>";}}}}MessageBox.Show("成功更新分析表!");tabControl_mTable.SelectTab(tabPage_Show);radioButton_Createmt.Checked = true;created = 1;ShowmTable(creamTable);}else{MessageBox.Show("请先点击“开始添加”创建表格!","错误提示",MessageBoxButtons.OK, MessageBoxIcon.Exclamation);}}public void ShowmTable(string[,] mTable)//显示分析表{listView_mtableshow.Clear();ColumnHeader colHeader;ListViewItem lvi;ListViewItem.ListViewSubItem lvsi;for (int i = 1; i <= mTable.GetLength(1); i++){colHeader = new ColumnHeader();colHeader.Text = i.ToString();listView_mtableshow.Columns.Add(colHeader);}for (int i = 0; i < mTable.GetLength(0); i++){lvi = new ListViewItem();lvi.Text = mTable[i, 0];for (int j = 1; j < mTable.GetLength(1); j++){lvsi = new ListViewItem.ListViewSubItem();lvsi.Text = mTable[i, j];lvi.SubItems.Add(lvsi);}listView_mtableshow.Items.Add(lvi);}}private void miFileNew_Click(object sender, EventArgs e)//菜单新建分析表{tabControl_mTable.SelectTab(tabPage_Edit);}3.实现打开、保存分析表、保存分析结果的功能(1)添加主菜单menuStrip_Main两个Item:miFile文件、miGraAnylysis语法分析。
LL1语法分析器_B12040921
实验报告
(2014/2015 学年第二学期)
课程名称 实验名称 实验时间 指导单位 指导教师
编译原理
语法分析器的构造
2015
年 5 月 29 日
计算机学院软件工程系
蒋凌云
学生姓名 学院(系)
Cjj
班级学号
计算机学院 专业
B--------NIIT
成绩
批阅人
日期
.
.
实验报告
实验名称
语法分析器的构造
self.firstSet,
def Judge(self, string): """判断字符串是否为当前文法的句子 """ isMatch = False
1
.
analyseStack = ["#", self.start] StringStack = list(string) + ["#"] print u"="*25,u"判断字符串=%s"%string,u"="*25 print "%-30s%-12s%s"%(u"分析栈",u"余留输入串",u"所用生成式") try:
classAnalyseMachine(object):
def __init__(self): pass
def load(self, Grammers): """载入文法规则 参数 Grammers: 文法的规则列表 """
self.Grammers = Grammers self.noLeftRecursionGrammers = self.__NoLeftRecursion(self.Grammers) self.start = self.Grammers[0][0] self.nChars = self.__GetVn(self.noLeftRecursionGrammers) self.tChars = self.__GetVt(self.noLeftRecursionGrammers) self.firstSet = self.FirstSet(self.noLeftRecursionGrammers) self.followSet = self.FollowSet(self.noLeftRecursionGrammers) self.analyseTable = self.AnalyseTable(self.noLeftRecursionGrammers, self.followSet)
编译原理实验报告3-LL(1)文法构造
实验3 LL(1)文法构造一、实验目的熟悉LL(1)文法的分析条件,了解LL(1)文法的构造方法。
二、实验内容1、编制一个能够将一个非LL(1)文法转换为LL(1)文法;2、消除左递归;3、消除回溯。
三、实验要求1、将一个可转换非LL(1)文法转换为LL(1)文法,要经过两个阶段,1)消除文法左递归,2)提取左因子,消除回溯。
2、提取文法左因子算法:1)对文法G的所有非终结符进行排序2)按上述顺序对每一个非终结符Pi依次执行:for( j=1; j< i-1;j++)将Pj代入Pi的产生式(若可代入的话);消除关于Pi的直接左递归:Pi -> Piα|β ,其中β不以Pi开头,则修改产生式为:Pi —> βPi′Pi′—>αPi′|ε3)化简上述所得文法。
3、提取左因子的算法:A—>δβ1|δβ2|…|δβn|γ1|γ2|…|γm(其中,每个γ不以δ开头) 那么,可以把这些产生式改写成A —>δA′|γ1| γ2…|γmA′—>β1|β2|…|βn4、利用上述算法,实现构造一个LL(1)文法:1)从文本文件g.txt中读入文法,利用实验1的结果,存入实验1设计的数据结构;2)设计函数remove_left_recursion()和remove_left_gene()实现消除左递归和提取左因子算法,分别对文法进行操作,消除文法中的左递归和提出左因子;3)整理得到的新文法;4)在一个新的文本文件newg.txt输出文法,文法输出按照一个非终结符号一行,开始符号引出的产生式写在第一行,同一个非终结符号的候选式用“|”分隔的方式输出。
四、实验环境PC微机DOS操作系统或Windows操作系统Turbo C程序集成环境或VisualC++ 程序集成环境五、实验步骤1、学习LL(1)文法的分析条件;2、学习构造LL(1)文法的算法;3、结合实验1给出的数据结构,编程实现构造LL(1)文法的算法;4、结合实验1编程和调试实现对一个具体文法运用上述算法,构造它的LL(1)文法形式;5、把实验结果写入一个新建立的文本文件。
编译原理-LL(1)文法源代码(实验三)
一、实验目的及要求1.掌握LL(1)分析法的基本原理;2.掌握LL(1)分析表的构造方法;3.用LL(1)分析法分析高级语言表达式。
4、了解LL(1)分析器的工作过程。
文法:无二义性的算术表达式的文法(1)把词法分析作为语法分析的子程序实现(5分)(2)独立的语法分析程序(4分)(3)对表达式文法消除左递归、构造LL(1)分析表(4)LL(1)分析表可以直接输入(4分),也可以用程序实现(5分)(5)给一个表达式,给出分析过程(分析栈、输入串、所用规则)(4分)(6)生成一个棵语法树(5分)用二叉树的形式表示出来二、实验内容及原理1、实验原理(1)、LL(1)文法的定义LL(1)分析法属于确定的自顶向下分析方法。
LL(1)的含义是:第一个L表明自顶向下分析是从左向右扫描输入串,第2个L表明分析过程中将使用最左推导,1表明只需向右看一个符号便可决定如何推导,即选择哪个产生式(规则)进行推导。
LL(1)文法的判别需要依次计算FIRST集、FOLLOW集和SELLECT集,然后判断是否为LL(1)文法,最后再进行句子分析。
需要预测分析器对所给句型进行识别。
即在LL(1)分析法中,每当在符号栈的栈顶出现非终极符时,要预测用哪个产生式的右部去替换该非终极符;当出现终结符时,判断其与剩余输入串的第一个字符是否匹配,如果匹配,则继续分析,否则报错。
LL(1)分析方法要求文法满足如下条件:对于任一非终极符A的两个不同产生式A→α,A→β,都要满足下面条件:SELECT(A→α)∩SELECT(A→β)=∅(2)、预测分析表构造LL(1)分析表的作用是对当前非终极符和输入符号确定应该选择用哪个产生式进行推导。
它的行对应文法的非终极符,列对应终极符,表中的值有两种:一是产生式的右部的字符串,一是null。
若用M表示LL(1)分析表,则M可表示如下:M: VN×VT→P∪{Error}M(A, t) = A→α,当t∈select(A→α) ,否则M(A, t) = Error其中P表示所有产生式的集合。
编译原理(3)语法_4(自顶向下语法分析:LL(1)分析法)
课本例题3.8 第二步:计算非终结符的FOLLOW集合
G[E]: E→TE' E'→ + TE' | ε T→FT' T'→*FT' | ε F→(E) | i ③由E→TE' 知FOLLOW(E) ⊂ FOLLOW(E' ), 即FOLLOW(E' ) = {),#}; 由E→TE ' 且E ' → ε知FOLLOW(E)FOLLOW(T),即 FOLLOW(T) = {+,),#};
特别是当Y1~Yk均含有ε产生式时,应把ε也加到FIRST(X)中。
课本例题3.8 第一步:计算非终结符的FIRST集合 例3.8 试构造表达式文法G[E]的LL(1)分析表,其中: G[E]: E→TE' E'→ + TE' | ε T→FT' T'→*FT' | ε F→(E) | i
[解答] 首先构造FIRST集,步骤如下: ① FIRST(E') = {+, ε}; FIRST(T') = {*, ε}; FIRST(F) = {(, i}; ② T→F… 和E→T…知:FIRST(F) ⊂ FIRST(T) ⊂ FIRST(E) 即有FIRST(F) = FIRST(T) = FIRST(E) = {(,i}。
编译原理课程设计LL文法分析器
目录引言 (1)第一章概述 (4)1.1设计内容 (4)1.2设计要求 (4)第二章设计的基本原理 (4)2.1预测分析表的构成原理 (4)2.2预测分析程序的生成 (5)第三章程序设计 (5)3.1总体方案设计 (6)3.2各模块设计 (6)第四章程序测试 (7)附录程序清单 (8)课 程 设 计L L (1)文法分析器2010 年 12 月设计题目学 号 专业班级 学生姓名指导教师合肥工业大学课程设计任务书第一章概述1.1 设计内容:1:预测分析表自动构造程序的实现2:预测分析程序的实现1.2 设计要求1:设计内容及要求:对于任意输入的一个LL(1)文法,构造其预测分析表。
要求:首先实现集合FIRST(X)构造算法和集合FOLLOW(A)构造算法,再实现教材P.79给出的预测分析表构造算法。
程序显示输出预测分析表或输出到指定文件中。
2:设计内容及要求:对文法 G: E→E+T|T 按教材P.76表4.1构造出G的预测分析程序,T→T*F|F 程序显示输出如P.78那样的匹配过程。
F→(E)|i第二章设计的基本原理2.1预测分析表的构成原理对于任意给定的LL(1) 文法G,为了构造它的预测分析表M,我们就必须构造与文法G有关的集合First和fellow.首先我们对每一个X∈V T U Vn ,构造FIRST(X),办法是,连续使用下面的规则,直至每个集合FIRST不再增大为止.(1)若X∈V T,,则FIRST(X)={X}.(2)若X∈Vn ,且有产生式X->a……,则把a加入到FIRST(X)中,若X->ε,也是一条产生式,则把ε也加到FIRST(X)中.(3)若X->Y……是一个产生式且Y∈Vn,则把FIRST(Y)中所有非ε-元素都加到FIRST(X)中,若X->Y1Y2……Y K ,是一个连续的产生式, Y1Y2……Y i-1 都是非终结符,而且,对于任何j,1≤j≤i-1,FIRST(Y j) 都含有ε(即Y1Y2……Y i-1=>ε),则把FIRST(Y i) 中的所有非ε-元素都加到FIRST(X)中,特别是,若所有的FIRST(Y j)均含有ε,j=1,2,……,k,则把ε加到FIRST(X)中.对于文法G中每个非终结符A构造FOLLOW(A)的办法是,连续使用下面的规则,直到每个FOLLOW不在增大为止.(1)对于文法的开始符号S,置#于FOLLOW(S)中;(2)若A->aBb是一个产生式,则把FIRST(b)\{ ε}加至FOLLOW(B)中;(3)若A->aB是一个产生式,或A->aBb是一个产生式而b=>ε(即ε∈FIRST( b))则把FOLLOW(A)加至FOLLOW(B)中构造分析表M的算法是:(1)对文法G的每个产生式A->a执行第二步和第三步;(2)对每个终结符a∈FIRST(a), 把A->a加至M[A,a]中;(3)若ε∈FIRST(a),则把任何b∈FOLLOW(A)把A->a加至M[A,b]中;(4)把所有无定义的M[A,a]标上出错标志.2.2 预测分析程序的生成预测分析程序的总控程序在任何时候都是按STACK栈顶符号X和当前的输入符号行事的,对于任何(X,a),总控程序每次都执行下述三种可能的动作之一;(1)若X=a=”#”,则宣布分析成功,停止分析过程.(2)若X=a≠”#”,则把X从STACK栈顶逐出,让a指向下一个输入符号.(3)若X是一个非终结符,则查看分析表M,若M[A,a]中存放着关于X的一个产生式,那么,首先把X逐出STACK栈顶,然后,把产生式的右部符号串按反序一一推进STACK栈(若右部符号为ε,则意味着不推什么东西进栈).在把产生式的右部符号推进栈的同时应做这个产生式相应得语义动作,若M[A,a]中存放着”出错标志”,则调用出错诊察程序ERROR.第三章程序设计3.1 总体方案设计在看到这个题目后,首先想到的就是要分模块进行设计.(1)第一模块:把文本文件中的LL(1)文法读入到程序中grammer类中的数组成员g中.(2)第二模块:通过对已输入到数组g中的文法,进行扫描,把相应的非终结符和终结符输入到非终结符数组VN中和终结符数组VT中.(3)第三模块:生成所有的非终结符的FIRST集合(4)第四模块:生成所有的非终结符的FOLLOW集合(5)第五模块:生成文法的预测分析表(6)第六模块:生成文法的预测分析程序通过这六模块的设计,最后可连接成一个预测分析程序.3.2 各模块程序设计首先要建立两个类,分别是grammer类和SeqStack类Grammer类中有保存从文本文件读入的LL(1)文法,用一个二维字符数组g保存, 保存非终结符的字符数组VN,保存终结符的字符数组VT,文法开始符号字符变量begin ,元素对应first集合中的有空字符的非终结符数组emptychar,预测分析表为二维整形数组form.SeqStack类主要的作用为在预测分析时作为符号栈.(1)第一模块的设计:对于这个模块的设计,就是用输入流对文本文件中的文法进行读入当读到字符’|’或’/n’时意味着一个产生式已结束,那么相应数组中相应的下标变量+1;数组g[i][0]所存放着这个产生式对应的字符的数量.g[i][1]为这个产生式左边的非终结符.(2)第二模块的设计:在已把文法读入到数组g中后,那么相应的g[i][0]为每个产生式的非终结符,若此非终结符不在数组VN中,那么把此字符加入到VN中对终结符的读入,则是从每个g[i][2]开始扫描,若此字符不在非终结符数组VN中,也不在VT 中,那么就把此字符加入到VT中.(3)第三模块的设计:生成FIRST集合这个算法基本上按照书上的原理,如对每个产生式进行扫描,当扫描到非终结符时,把此字符加入到相应非终结符的FIRST集合中,若遇到非终结符时,需把此非终结符的FIRST集合中除了空字符外全加入到对应的非终结符的first集合中,这就涉及到算法的一个递归算法,我是利用在函数的参数表中设置俩个参数,一个是FIRST数组,另外一个是非终结符,在用到递归时,我是利用把FIRST数组设为本产生式第一个非终结符的FIRST数组,而非终结符参数则设置为在扫描产生式右部过程中所遇到的非终结符.然后通过循环对这个文法中的所有非终结符进行函数的调用,这样来求到所有非终结符的FIRST集合.(4)第四模块的设计:生成FOLLOW集合这个算法,关键就是求某非终结符A的FOLLOW集合,那么就是扫描到A后面的字符,若为终结符,则把此终结符加入到A的FOLLOW集合,那么若为非终结符,则把此非终结符的FIRST集合加入到A的FOLLOW集合中,若A为某个产生式最后一个字符,则把这个产生式的第一个字符的FOLLOW集合加入到A的FOLLOW集合中,在这个算法的过程中叶涉及到运用递归,此递归的方法同样与上述求FIRST集合中运用的递归方法类似(5)第五模块的设计:求预测分析表的过程同样是对每个产生式进行扫描,对每个产生式的FIRST集合中的元素,都把相应的产生式的编号写入到对应的预测分析表中对应的位置(6)第六模块的设计:生成预测分析程序,就是把’#’和文法开始符号进入到栈中,然后把相应的分析语句中当前的输入符号对应,在每一步过程中按照书上讲到的原理,在预测分析表中找到相应的产生式.第四章程序测试附录程序清单class SeqStack;//声明栈类class grammer//定义grammer类,此类的作用把文本文件中的文法保存,并且产生这个文法的非终结符集合,终结符集合,first集合,fellow集合,预测分析表等{public:grammer();~grammer();void openfile(char *);void prepareform();void buildform();void buildProcess(SeqStack& ss);private:char begin;char *vt;char **g;char *vn;char **first;//这时所有非终结符的first集合char **fellow;//这是所有非终结符的fellow集合char *emptychar;//这个集合中的元素为对应first集合中的有空字符的非终结符int **form;//这是对应文法的预测分析表int count;void buildVn();void buildVt();void buildemptychar();void buildfirst();void buildfellow();void fellowzh(char *,char);void firstzh(char *,char);int search(char *,char);void printform();void outputblank(int);};const int stackIncreament=20;class SeqStack//定义SeqStack类,这个类的主要作用是在对语句的预测分析过程中作符号栈{public:friend grammer;SeqStack(int sz=50);~SeqStack();void Push(char x);bool Pop(char& x);bool getTop(char& x);bool IsEmpty();bool IsFull();int getSize();void MakeEmpty();void showPlay();private:char *elements;int top;int maxSize;void overflowProcess();};#include<fstream>#include<iostream>#include<stdio.h>#include<stdlib.h>#include<assert.h>#include<windows.h>#include "编译.h"using namespace std;grammer::grammer(){count=0;}grammer::~grammer(){int i;for(i=0;i<count;i++)delete g[i];delete g;delete vt;delete vn;}void grammer::openfile(char *file)//这个函数的主要功能在于对文本文件中的文法进行读入,并保存在对应的数组中,一并产生对应文法的终结符集合和非终结符集合{char ch;int i,j;ifstream infile(file,ios::in);//定义文件流if(!infile){cout<<"open error!"<<endl;exit(1);}while((ch=infile.get())!=EOF){if(ch=='-'){if((ch=infile.get())=='>'){count++;}else{infile.seekg(-1,ios::cur);}}else if(ch=='|'){count++;}else{}}g=new char *[count];for(i=0;i<count;i++){g[i]=new char [15];g[i][0]=0;}infile.clear();//能重新激活文件流infile.seekg(0,ios::beg);//定位i=0;j=1;while((ch=infile.get())!=EOF){if(ch=='-'){if(infile.get()!='>'){g[i][j]=ch;g[i][0]++;j++;if(ch==EOF)break;elseinfile.seekg(-1,ios::cur);}}else if(ch=='|'){i++;g[i][1]=g[i-1][1];g[i][0]=1;j=2;}else if(ch=='\n'){infile.seekg(-3,ios::cur);ch=infile.get();if(ch!='\n'){i++;j=1;}infile.seekg(2,ios::cur);}else{g[i][j]=ch;g[i][0]++;j++;}}cout<<count<<endl;for(i=0;i<count;i++){for(j=1;j<=g[i][0];j++){cout<<g[i][j]<<" ";}cout<<endl;}buildVn();//建立非终结符集合buildVt();//建立终结符集合}void grammer::buildVn()int i=1,j=0;vn=new char[count+1];vn[i]=g[j++][1];for(;j<count;j++)if(g[j][1]!=g[j-1][1]){i++;vn[i]=g[j][1];}vn[0]=i;begin=vn[1];cout<<"本文法开始符为:"<<begin<<endl;cout<<"本文法非终结符为:"<<" ";for(i=1;i<=vn[0];i++)cout<<vn[i]<<" ";cout<<endl;}void grammer::buildVt(){int i=1,j=0,t;char ch;vt=new char[100];vt[0]=0;for(;j<count;j++){t=2;for(;t<=g[j][0];t++){ch=g[j][t];if(!search(vt,ch)&&!search(vn,ch)){vt[i++]=g[j][t];vt[0]++;}}}cout<<"本文法终结符为:"<<" ";for(i=1;i<=vt[0];i++)cout<<vt[i]<<" ";cout<<endl;}int grammer::search(char *a,char ch)//搜索函数,用于在指定数组中对指定字符进行搜寻,若存在输出对应的序号,若不在则输出0for(int i=1;i<=a[0];i++){if(ch==a[i])return i;}return 0;}void grammer::buildemptychar()//建立对应first集合中含有空字符的的非终结符集合{int i=0,j=2,t=1;bool flag=true,flag1;emptychar=new char[vn[0]+1];emptychar[0]=0;for(;i<count;i++){if(g[i][2]=='@'&&!search(emptychar,g[i][1])){emptychar[t++]=g[i][1];emptychar[0]++;}}while(flag){flag=false;for(i=0;i<count;i++){for(j=2;j<=g[i][0];j++){if(search(emptychar,g[i][j])){flag1=true;}else{flag1=false;break;}}if(flag1==true&&!search(emptychar,g[i][1])){emptychar[t++]=g[i][1];emptychar[0]++;flag=true;}}cout<<"First集中含有空字符的有: ";for(i=1;i<=emptychar[0];i++){cout<<emptychar[i]<<" ";}cout<<endl;}void grammer::buildfirst(){int i;first=new char* [vn[0]];for(i=0;i<vn[0];i++){first[i]=new char[vt[0]+1];}for(i=0;i<vn[0];i++)first[i][0]=0;for(i=1;i<=vn[0];i++){firstzh(first[i-1],vn[i]);}}void grammer::buildfellow(){int i,j;fellow=new char* [vn[0]];for(i=0;i<vn[0];i++){fellow[i]=new char[vt[0]+1];}for(i=0;i<vn[0];i++)fellow[i][0]=0;for(i=1;i<=vn[0];i++){fellowzh(fellow[i-1],vn[i]);}for(i=0;i<vn[0];i++){cout<<vn[i+1]<<"的fellow集合为: ";for(j=1;j<=fellow[i][0];j++){cout<<fellow[i][j]<<" ";cout<<endl;}}void grammer::firstzh(char *a,char ch)//对指定非终结符建立对应的first集合{int i,j,t=1;for(i=0;i<count;i++){for(j=2;j<=g[i][0];j++){if(g[i][1]==ch){if(!search(vn,g[i][j])&&g[i][j]!='@')//找到对应产生式中的终结符时{if(!search(a,g[i][j])){a[t++]=g[i][j];a[0]++;}break;}else if(g[i][j]!='@')//找到对应的产生式中的非终结符时{firstzh(a,g[i][j]);//用递归的方法算出此非终结符的first集合if(!search(emptychar,g[i][j]))//当此非终结符的first集合中无空字符时,对此产生式扫描完毕break;}else{}}elsebreak;}}}void grammer::fellowzh(char *a,char ch)//对指定非终结符建立fellow集合{int i,j,k,m,n,t=a[0];if(ch==begin)//把#字符放入开始符的fellow集合{if(!search(a,'#'))a[++t]='#';a[0]++;}}for(i=0;i<count;i++){for(j=2;j<=g[i][0];j++){if(g[i][j]==ch){for(m=j;m<=g[i][0];m++){if(m==g[i][0]){if(g[i][1]!=ch)fellowzh(a,g[i][1]);//发现要求的非终结符在此产生式的末尾时elsebreak;}else{if(!search(vn,g[i][m+1]))//当此非终结符后面的字符为终结字符时,放入对应的fellow集合{if(!search(a,g[i][m+1])){a[++t]=g[i][m+1];a[0]++;}break;}else{k=search(vn,g[i][m+1]);//把此非终结符后面的非终结符的first集合中的元素放入对应的fellow集合for(n=1;n<=first[k-1][0];n++){if(!search(a,first[k-1][n])){a[++t]=first[k-1][n];a[0]++;}}if(!search(emptychar,g[i][m+1]))break;}}}}}}void grammer::prepareform(){int i,j;buildemptychar();buildfirst();buildfellow();for(i=0;i<vn[0];i++){if(search(emptychar,vn[i+1])){j=first[i][0];first[i][++j]='@';first[i][0]++;}}for(i=0;i<vn[0];i++){cout<<vn[i+1]<<"的first集合为: ";for(j=1;j<=first[i][0];j++){cout<<first[i][j]<<" ";}cout<<endl;}}void grammer::buildform(){int i,j,k,t,m,n;bool flag;form=new int*[vn[0]];for(i=0;i<vn[0];i++)form[i]=new int[vt[0]];for(i=0;i<vn[0];i++){for(j=0;j<vt[0];j++){form[i][j]=-1;}for(i=0;i<count;i++){t=search(vn,g[i][1]);flag=true;for(j=2;j<=g[i][0];j++){k=search(vt,g[i][j]);//若发现这个产生式的first集合中的对应的终结符时if(k){if(g[i][j]!='@'){if(form[t-1][k-1]!=-1){cout<<"本终结符错误!"<<endl;exit(1);}form[t-1][k-1]=i;flag=false;break;}else//若发现对应产生式中的first集合中含有空字符时{for(m=1;m<=fellow[t-1][0];m++){if(fellow[t-1][m]!='#')//可把对应的非终结符的fellow集合中的元素对应的表项中填入该产生式的标号{n=search(vt,fellow[t-1][m]);if(form[t-1][n-1]!=-1){cout<<"有终结符@本fellow集合中非#错误!"<<endl;exit(1);}form[t-1][n-1]=i;}else{if(form[t-1][k-1]!=-1){cout<<"有终结符@本fellow集合中#错误!"<<endl;exit(1);}form[t-1][k-1]=i;}flag=false;break;}}else//找到该产生式中的非终结符时{k=search(vn,g[i][j]);for(m=1;m<=first[k-1][0];m++){if(first[k-1][m]!='@'){n=search(vt,first[k-1][m]);if(form[t-1][n-1]!=-1){cout<<"本first集合中非@!"<<endl;exit(1);}form[t-1][n-1]=i;}else{}}if(!search(first[k-1],'@'))//若在此终结符对应的first集合中无空字符时{flag=false;break;}}}if(flag==true)//说明该产生式对应的first集合中含有空字符{for(m=1;m<=fellow[t-1][0];m++){if(fellow[t-1][m]!='#'){n=search(vt,fellow[t-1][m]);if(form[t-1][n-1]!=-1){cout<<"本fellow集合中非#错误!"<<endl;exit(1);}form[t-1][n-1]=i;}{n=search(vt,'@');if(form[t-1][n-1]!=-1){cout<<"本fellow集合中#错误!"<<endl;exit(1);}form[t-1][n-1]=i;}}}}printform();}void grammer::outputblank(int i){int j;for(j=0;j<i;j++)cout<<" ";}void grammer::printform(){int i,j,k,t,m;cout<<"-----------------------------预测分析表如下所示------------------------------"<<endl;outputblank(6);for(i=1;i<=vt[0];i++){if(vt[i]=='@'){cout<<"#";outputblank(10);}else{cout<<vt[i];outputblank(10);}}cout<<endl;for(i=0;i<vn[0];i++){cout<<" "<<vn[i+1];outputblank(4);{k=form[i][j];if(k==-1){Sleep(500);cout<<"error";outputblank(6);}else{Sleep(1000);t=9-g[k][0];cout<<g[k][1]<<"->";for(m=2;m<=g[k][0];m++){cout<<g[k][m];}outputblank(t);}}cout<<endl;}}void grammer::buildProcess(SeqStack& ss) {char *dia,ch;int i,j,inlen,m,n,index=0,proc=0;bool flag=true,flag1=false;dia=new char[20];cout<<"请输入待分析的字符串"<<endl;cin>>dia;for(i=0,inlen=0;i<20;i++){if(dia[i]!='\0')inlen++;elsebreak;}cout<<"步骤";outputblank(8);cout<<"符号栈";outputblank(15);cout<<"输入串";outputblank(15);cout<<"所用产生式"<<endl;dia[inlen++]='#';ss.Push('#');ss.Push(begin);while(flag){Sleep(1500);cout<<proc;if(proc>=10)outputblank(11);elseoutputblank(12);++proc;ss.showPlay();outputblank(21-ss.getSize());for(j=index;j<inlen;j++)cout<<dia[j];outputblank(21-inlen+index);if(flag1==true){cout<<g[form[m-1][n-1]][1]<<"->";for(j=2;j<=g[form[m-1][n-1]][0];j++)cout<<g[form[m-1][n-1]][j];cout<<endl;}elsecout<<endl;ss.getTop(ch);m=search(vn,ch);if(dia[index]!='#')n=search(vt,dia[index]);elsen=search(vt,'@');if(search(vt,ch))//当栈顶是终结符{if(ch==dia[index])//且此终结符与输入串此时检测的终结符符号相同{index++;ss.Pop(ch);flag1=false;}else{cout<<"该字符串不能由该文法推出"<<endl;exit(1);}}else if(ch=='#'){if(ch==dia[index]){cout<<endl;cout<<"---------------------------------分析完成------------------------------"<<endl;flag=false;}else{cout<<"该字符串不能由该文法推出"<<endl;exit(1);}}else if(form[m-1][n-1]!=-1)//当栈顶的非终结符与输入串对应的终结符在预测分析表中相应的项存在时{if(g[form[m-1][n-1]][2]!='@'){ss.Pop(ch);for(i=g[form[m-1][n-1]][0];i>=2;i--){ss.Push(g[form[m-1][n-1]][i]);}}elsess.Pop(ch);flag1=true;}else{cout<<"该字符串不能由该文法推出"<<endl;exit(1);}}}void SeqStack::overflowProcess(){char *newArray=new char[maxSize+stackIncreament];if(newArray==NULL){cerr<<"存储分配失败!"<<endl;exit(1);}for(int i=0;i<=top;i++)newArray[i]=elements[i];maxSize=maxSize+stackIncreament;delete []elements;elements=newArray;}void SeqStack::Push(char x){if(IsFull()==true)overflowProcess();elements[++top]=x;}bool SeqStack::Pop(char &x){if(IsEmpty()==true)return false;x= elements[top--];return true;}bool SeqStack::getTop(char &x){if(IsEmpty()==true)return false;x=elements[top];return true;}void SeqStack::showPlay(){for(int i=0;i<=top;i++)cout<<elements[i];}SeqStack::SeqStack(int sz):top(-1),maxSize(sz) {elements=new char[maxSize];assert(elements!=NULL);}SeqStack::~SeqStack(){delete []elements;}bool SeqStack::IsEmpty(){return(top==-1)?true:false;}bool SeqStack::IsFull(){return (top==maxSize-1)?true:false;}int SeqStack::getSize(){return top+1;}void SeqStack::MakeEmpty(){top=-1;}#include<iostream>#include"编译.h"using namespace std;void main(){//char ch;grammer ga;SeqStack ss;char filename[20];cout<<"欢迎来到本LL(1)文法预测分析表自动生成程序"<<endl;cout<<endl;cout<<endl;cout<<"请输入文法所在的文件名"<<endl;cin>>filename;ga.openfile(filename);ga.prepareform();ga.buildform();ga.buildProcess(ss);}。
LL(1)文法分析表的构造和分析过程示例
LL(1)⽂法分析表的构造和分析过程⽰例在考完编译原理之后才弄懂,悲哀啊。
不过懂了就好,知识吗,不能局限于考试。
⽂法:E→TE'E'→+TE'|εT→FT 'T'→*FT'|εF→id| (E)⼀、⾸先判断是不是 LL(1)⽂法--------------------------------------------------------------------------------------------------------⽂法G的任意两个具有相同左部的产⽣式 A --> α|β满⾜下列条件:1、如果α和β不能同时推导出ε,则 FIRST(α)∩FIRST(β) = 空2、α和β⾄多有⼀个能推导出ε3、如果β --*--> ε ,则 FIRST(α)∩ FOLLOW(A)=空--------------------------------------------------------------------------------------------------------对于 E'→+TE'|ε,显然ε --> ε, First(+TE') = {+} ,Follow(E') = {{),#} 显然⼆者交集为空满⾜。
对于 F→id|(E) ,First(id) = {id} First((E)) = {(} 显然⼆者交集为空满⾜。
所以该⽂法是LL(1)⽂法。
⼆、计算出First集和Follow集参考:三、构建LL(1)分析表输⼊:⽂法G输出:分析表M步骤:1、对G中任意⼀个产⽣式 A --> α执⾏第2步和第3步2、for 任意a ∈ First(α),将 A --> α填⼊M[A,a]3、if ε∈ First(α) then 任意a ∈ Follow(A),将 A --> α填⼊M[A,a]if ε∈ First(α) & # ∈Follow(A), then 将 A --> α填⼊M[A,#] (觉得这步没⽤)4、将所有没有定义的M[A,b] 标上出错标志(留空也可以)--------------------------------------------------------------------------------------------------------过程就不赘述了,结果:四、分析过程步骤:1、如果 X = a = # 则分析成功并停机2、如果 X = a != # 则弹出栈顶符号X, 并将输⼊指针移到下⼀个符号上3、如果 X != a,查询分析表M[X,a] , 如果 M[X,a] = {X --> UVW},则⽤UVW (U在栈顶) 替换栈顶符号 X。
北邮编译原理LL(1)语法分析程序教学内容
LL(1)语法分析程序2010211306班赵雪莹(10211310)语法分析程序:该语法分析程序实现对算术表达式的语法分析,并且在对输入表达式进行分析的过程中,输出所采用的产生式。
该程序使用的是LL(1)语法分析程序,为给定文法构造预测分析表,并通过预测分析表对输入的表达式进行预测分析,并将栈顶状态和预测分析过程详细输出,如果匹配成功则接受,如果匹配不成功则返回错误信息。
给定文法的产生式:E->E+T | E-T | TT->T*F | T/F | FF-> id | (E) | num源代码:#include <iostream>#include <cstdio>#include <cstdlib>#include <cstring>#include <stack>using namespace std;struct Node1{char vn;char vt;char s[12];}MAP[22];//存储分析预测表每个位置对应的终结符,非终结符,产生式int k;charG[12][12]={"E->TR","R->+TR","R->-TR","R->e","T->FW","W->*FW","W->/FW","W->e" ,"F->(E)","F->i","F->n"};//存储文法中的产生式,用R代表E',W代表T',e代表空char VN[6]={'E','R','T','W','F'};//存储非终结符char VT[9]={'i','n','+','-','*','/','(',')','#'};//存储终结符char FOLLOW[12][12]={"(,i,n","+","-","),#","(,i,n","*","/","+,-,),#","(","i","n"};//存储文法中每个产生式对应的FOLLOW集合charRight[12][8]={"->TR","->+TR","->-TR","->e","->FW","->*FW","->/FW","->e","->(E)","-> i","->n"};stack <char> stak,stak1,stak2;bool compare(char *a,char *b){int i,la=strlen(a),j,lb=strlen(b);for(i=0;i<la;i++)for(j=0;j<lb;j++){if(a[i]==b[j])return 1;}return 0;}char *Find(char vn,char vt){int i;for(i=0;i<k;i++){if(MAP[i].vn==vn && MAP[i].vt==vt)return MAP[i].s;}return "error";}char * Analyse(char * word){char p,c,action[10],output[10];int i=1,l=strlen(word),j,k=0,l_act,m,x;printf("_______________________________________________________________ _________________\n");printf("\n 对符号串%s的分析过程\n",word);for(x=0;x<l;x++)//把用字母数字表示的输入串转换为token序列的表示方法{ c=word[x];if(((c<='z')&&(c>='a'))||((c<='Z')&&(c>='A')))word[x]='i';else if(c>='0'&&c<='9')word[x]='n';elseword[x]=c;}while(!stak.empty())//判断栈中是否为空,若不空就将栈顶元素与分析表匹配进行相应操作stak.pop();stak.push('#');//栈底标志stak.push('E');//起始符号先入栈printf(" 步骤栈顶元素输入串推到所用产生式或匹配\n");p=stak.top();while(p!='#')//查预测分析表将栈顶元素进行匹配,若栈顶元素与输入串匹配成功则向前匹配,否则生成式反序入栈{printf("%7d ",i++);p=stak.top();//从栈中弹出一个栈顶符号,由p记录并输出stak.pop();printf("%6c ",p);for(j=k,m=0;j<l;j++)//将未被匹配的剩余输入串输出output[m++]=word[j];output[m]='\0';printf("%10s",output);if(p==word[k]){if(p=='#')//若最后一个结束符号匹配说明输入表达式被接受,否则继续匹配{printf(" 接受\n");return "SUCCESS";}printf(" “%c”匹配\n",p);k++;}else{//将未被匹配的第一个字符与find函数的结果进行比较,在预测分析表中查找相应生成式strcpy(action,Find(p,word[k]));if(strcmp(action,"error")==0){printf(" 没有可用的产生式\n");return "ERROR";}printf(" %c%s\n",p,action);int l_act=strlen(action);if(action[l_act-1]=='e')continue;for(j=l_act-1;j>1;j--)stak.push(action[j]);}}if(strcmp(output,"#")!=0)//匹配不成功return "ERROR";}int main (){freopen("in.txt","r",stdin);char source[100];int i,j,flag,l,m;printf("\n*****R代表E',W代表T',e代表空*****\n\n");printf("算术表达式对应的文法产生式如下:\n");for(i=0;i<8;i++)printf(" %s\n",G[i]);printf("_______________________________________________________________ _________________\n");printf("\n该文法的FOLLOW集如下:\n"); //手动生成FOLLOW集合for(i=0;i<8;i++){printf(" FOLLOW(%s) = { %s }\n",G[i],FOLLOW[i]);}printf("_______________________________________________________________ _________________\n");for(i=0,k=0;i<11;i++)//通过FOLLOW集合生成预测分析表{l=strlen(FOLLOW[i]);for(j=0;j<l;j+=2){MAP[k].vn=G[i][0];MAP[k].vt=FOLLOW[i][j];strcpy(MAP[k].s,Right[i]);k++;}}printf("\n表达式文法的预测分析表如下:\n\n");printf(" ");for(i=0;i<9;i++)printf("%7c",VT[i]);printf("\n");for(i=0;i<5;i++){printf(" ---------------------------------------------------------------\n");printf("%7c",VN[i]);for(j=0;j<9;j++){for(m=0;m<k;m++){if(VN[i]==MAP[m].vn && VT[j]==MAP[m].vt){printf("%7s",MAP[m].s);break;}}if(m==k)printf(" ");}printf("\n");}while(cin>>source)//输入源文件串进行预测分析{printf("\n分析结果:%s\n\n",Analyse(source));}while(1);return 0;}将其改写LL(1)文法:手动生成的FOLLOW集合为:生成的预测分析表为:输入表达式:(通过文件输入)对表达式((a+b)*3/5)#首先转换为token序列,以i代表标识符,以n代表常量,然后进行预测分析:如果输入的是错误的表达式(少了一个括号),则:。
编译原理实验报告《LL(1)语法分析器构造》
《LL(1)分析器的构造》实验报告一、实验名称LL(1)分析器的构造二、实验目的设计、编制、调试一个LL(1)语法分析器,利用语法分析器对符号串的识别,加深对语法分析原理的理解。
三、实验内容和要求设计并实现一个LL(1)语法分析器,实现对算术文法:G[E]:E->E+T|TT->T*F|FF->(E)|i所定义的符号串进行识别,例如符号串i+i*i为文法所定义的句子,符号串ii+++*i+不是文法所定义的句子。
实验要求:1、检测左递归,如果有则进行消除;2、求解FIRST集和FOLLOW集;3、构建LL(1)分析表;4、构建LL分析程序,对于用户输入的句子,能够利用所构造的分析程序进行分析,并显示出分析过程。
四、主要仪器设备硬件:微型计算机。
软件:Code blocks(也可以是其它集成开发环境)。
五、实验过程描述1、程序主要框架程序中编写了以下函数,各个函数实现的作用如下:void input_grammer(string *G);//输入文法Gvoid preprocess(string *G,string *P,string &U,string &u,int &n,int &t,int &k);//将文法G预处理得到产生式集合P,非终结符、终结符集合U、u,int eliminate_1(string *G,string *P,string U,string *GG);//消除文法G中所有直接左递归得到文法GGint* ifempty(string* P,string U,int k,int n);//判断各非终结符是否能推导为空string* FIRST_X(string* P,string U,string u,int* empty,int k,int n);求所有非终结符的FIRST集string FIRST(string U,string u,string* first,string s);//求符号串s=X1X2...Xn的FIRST集string** create_table(string *P,string U,string u,int n,int t,int k,string* first);//构造分析表void analyse(string **table,string U,string u,int t,string s);//分析符号串s2、编写的源程序#include<cstdio>#include<cstring>#include<iostream>using namespace std;void input_grammer(string *G)//输入文法G,n个非终结符{int i=0;//计数char ch='y';while(ch=='y'){cin>>G[i++];cout<<"继续输入?(y/n)\n";cin>>ch;}}void preprocess(string *G,string *P,string &U,string &u,int &n,int &t,int &k)//将文法G预处理产生式集合P,非终结符、终结符集合U、u,{int i,j,r,temp;//计数char C;//记录规则中()后的符号int flag;//检测到()n=t=k=0;for( i=0;i<50;i++) P[i]=" ";//字符串如果不初始化,在使用P[i][j]=a时将不能改变,可以用P[i].append(1,a)U=u=" ";//字符串如果不初始化,无法使用U[i]=a赋值,可以用U.append(1,a)for(n=0;!G[n].empty();n++){ U[n]=G[n][0];}//非终结符集合,n为非终结符个数for(i=0;i<n;i++){for(j=4;j<G[i].length();j++){if(U.find(G[i][j])==string::npos&&u.find(G[i][j])==string::npos) if(G[i][j]!='|'&&G[i][j]!='^')//if(G[i][j]!='('&&G[i][j]!=')'&&G[i][j]!='|'&&G[i][j]!='^')u[t++]=G[i][j];}}//终结符集合,t为终结符个数for(i=0;i<n;i++){flag=0;r=4;for(j=4;j<G[i].length();j++){P[k][0]=U[i];P[k][1]=':';P[k][2]=':';P[k][3]='=';/* if(G[i][j]=='('){ j++;flag=1;for(temp=j;G[i][temp]!=')';temp++);C=G[i][temp+1];//C记录()后跟的字符,将C添加到()中所有字符串后面}if(G[i][j]==')') {j++;flag=0;}*/if(G[i][j]=='|'){//if(flag==1) P[k][r++]=C;k++;j++;P[k][0]=U[i];P[k][1]=':';P[k][2]=':';P[k][3]='=';r=4;P[k][r++]=G[i][j];}else{P[k][r++]=G[i][j];}}k++;}//获得产生式集合P,k为产生式个数}int eliminate_1(string *G,string *P,string U,string *GG)//消除文法G1中所有直接左递归得到文法G2,要能够消除含有多个左递归的情况){string arfa,beta;//所有形如A::=Aα|β中的α、β连接起来形成的字符串arfa、betaint i,j,temp,m=0;//计数int flag=0;//flag=1表示文法有左递归int flagg=0;//flagg=1表示某条规则有左递归char C='A';//由于消除左递归新增的非终结符,从A开始增加,只要不在原来问法的非终结符中即可加入for(i=0;i<20&&U[i]!=' ';i++){ flagg=0;arfa=beta="";for(j=0;j<100&&P[j][0]!=' ';j++){if(P[j][0]==U[i]){if(P[j][4]==U[i])//产生式j有左递归{flagg=1;for(temp=5;P[j][temp]!=' ';temp++) arfa.append(1,P[j][temp]);if(P[j+1][4]==U[i]) arfa.append("|");//不止一个产生式含有左递归}else{for(temp=4;P[j][temp]!=' ';temp++) beta.append(1,P[j][temp]);if(P[j+1][0]==U[i]&&P[j+1][4]!=U[i]) beta.append("|");}}}if(flagg==0)//对于不含左递归的文法规则不重写{GG[m]=G[i]; m++;}else{flag=1;//文法存在左递归GG[m].append(1,U[i]);GG[m].append("::=");if(beta.find('|')!=string::npos) GG[m].append("("+beta+")");else GG[m].append(beta);while(U.find(C)!=string::npos){C++;}GG[m].append(1,C);m++;GG[m].append(1,C);GG[m].append("::=");if(arfa.find('|')!=string::npos) GG[m].append("("+arfa+")");else GG[m].append(arfa);GG[m].append(1,C);GG[m].append("|^");m++;C++;}//A::=Aα|β改写成A::=βA‘,A’=αA'|β,}return flag;}int* ifempty(string* P,string U,int k,int n){int* empty=new int [n];//指示非终结符能否推导到空串int i,j,r;for(r=0;r<n;r++) empty[r]=0;//默认所有非终结符都不能推导到空int flag=1;//1表示empty数组有修改int step=100;//假设一条规则最大推导步数为100步while(step--){for(i=0;i<k;i++){r=U.find(P[i][0]);if(P[i][4]=='^') empty[r]=1;//直接推导到空else{for(j=4;P[i][j]!=' ';j++){if(U.find(P[i][j])!=string::npos){if(empty[U.find(P[i][j])]==0) break;}else break;}if(P[i][j]==' ') empty[r]=1;//多步推导到空else flag=0;}}}return empty;}string* FIRST_X(string* P,string U,string u,int* empty,int k,int n) {int i,j,r,s,tmp;string* first=new string[n];char a;int step=100;//最大推导步数while(step--){// cout<<"step"<<100-step<<endl;for(i=0;i<k;i++){//cout<<P[i]<<endl;r=U.find(P[i][0]);if(P[i][4]=='^'&&first[r].find('^')==string::npos) first[r].append(1,'^');//规则右部首符号为空else{for(j=4;P[i][j]!=' ';j++){a=P[i][j];if(u.find(a)!=string::npos&&first[r].find(a)==string::npos)//规则右部首符号是终结符{first[r].append(1,a);break;//添加并结束}if(U.find(P[i][j])!=string::npos)//规则右部首符号是非终结符,形如X::=Y1Y2...Yk{s=U.find(P[i][j]);//cout<<P[i][j]<<":\n";for(tmp=0;first[s][tmp]!='\0';tmp++){a=first[s][tmp];if(a!='^'&&first[r].find(a)==string::npos)//将FIRST[Y1]中的非空符加入first[r].append(1,a);}}if(!empty[s]) break;//若Y1不能推导到空,结束}if(P[i][j]==' ')if(first[r].find('^')==string::npos)first[r].append(1,'^');//若Y1、Y2...Yk都能推导到空,则加入空符号}}}return first;}string FIRST(string U,string u,string* first,string s)//求符号串s=X1X2...Xn的FIRST集{int i,j,r;char a;string fir;for(i=0;i<s.length();i++){if(s[i]=='^') fir.append(1,'^');if(u.find(s[i])!=string::npos&&fir.find(s[i])==string::npos){ fir.append(1,s[i]);break;}//X1是终结符,添加并结束循环if(U.find(s[i])!=string::npos)//X1是非终结符{r=U.find(s[i]);for(j=0;first[r][j]!='\0';j++){a=first[r][j];if(a!='^'&&fir.find(a)==string::npos)//将FIRST(X1)中的非空符号加入fir.append(1,a);}if(first[r].find('^')==string::npos) break;//若X1不可推导到空,循环停止}if(i==s.length())//若X1-Xk都可推导到空if(fir.find(s[i])==string::npos) //fir中还未加入空符号fir.append(1,'^');}return fir;}string** create_table(string *P,string U,string u,int n,int t,int k,string* first)//构造分析表,P为文法G的产生式构成的集合{int i,j,p,q;string arfa;//记录规则右部string fir,follow;string FOLLOW[5]={")#",")#","+)#","+)#","+*)#"};string **table=new string*[n];for(i=0;i<n;i++) table[i]=new string[t+1];for(i=0;i<n;i++)for(j=0;j<t+1;j++)table[i][j]=" ";//table存储分析表的元素,“ ”表示error for(i=0;i<k;i++){arfa=P[i];arfa.erase(0,4);//删除前4个字符,如:E::=E+T,则arfa="E+T"fir=FIRST(U,u,first,arfa);for(j=0;j<t;j++){p=U.find(P[i][0]);if(fir.find(u[j])!=string::npos){q=j;table[p][q]=P[i];}//对first()中的每一终结符置相应的规则}if(fir.find('^')!=string::npos){follow=FOLLOW[p];//对规则左部求follow()for(j=0;j<t;j++){if((q=follow.find(u[j]))!=string::npos){q=j;table[p][q]=P[i];}//对follow()中的每一终结符置相应的规则}table[p][t]=P[i];//对#所在元素置相应规则}}return table;}void analyse(string **table,string U,string u,int t,string s)//分析符号串s {string stack;//分析栈string ss=s;//记录原符号串char x;//栈顶符号char a;//下一个要输入的字符int flag=0;//匹配成功标志int i=0,j=0,step=1;//符号栈计数、输入串计数、步骤数int p,q,r;string temp;for(i=0;!s[i];i++){if(u.find(s[i])==string::npos)//出现非法的符号cout<<s<<"不是该文法的句子\n";return;}s.append(1,'#');stack.append(1,'#');//’#’进入分析栈stack.append(1,U[0]);i++;//文法开始符进入分析栈a=s[0];//cout<<stack<<endl;cout<<"步骤分析栈余留输入串所用产生式\n";while(!flag){// cout<<"步骤分析栈余留输入串所用产生式\n"cout<<step<<" "<<stack<<" "<<s<<" ";x=stack[i];stack.erase(i,1);i--;//取栈顶符号x,并从栈顶退出//cout<<x<<endl;if(u.find(x)!=string::npos)//x是终结符的情况{if(x==a){s.erase(0,1);a=s[0];//栈顶符号与当前输入符号匹配,则输入下一个符号cout<<" \n";//未使用产生式,输出空}else{cout<<"error\n";cout<<ss<<"不是该文法的句子\n";break;}}if(x=='#'){if(a=='#') {flag=1;cout<<"成功\n";}//栈顶和余留输入串都为#,匹配成功else{cout<<"error\n";cout<<ss<<"不是该文法的句子\n";break;}}if(U.find(x)!=string::npos)//x是非终结符的情况{p=U.find(x);q=u.find(a);if(a=='#') q=t;temp=table[p][q];cout<<temp<<endl;//输出使用的产生式if(temp[0]!=' ')//分析表中对应项不为error{r=9;while(temp[r]==' ') r--;while(r>3){if(temp[r]!='^'){stack.append(1,temp[r]);//将X::=x1x2...的规则右部各符号压栈i++;}r--;}}else{cout<<"error\n";cout<<ss<<"不是该文法的句子\n";break;}}step++;}if(flag) cout<<endl<<ss<<"是该文法的句子\n"; }int main(){int i,j;string *G=new string[50];//文法Gstring *P=new string[50];//产生式集合Pstring U,u;//文法G非终结符集合U,终结符集合uint n,t,k;//非终结符、终结符个数,产生式数string *GG=new string[50];//消除左递归后的文法GGstring *PP=new string[50];//文法GG的产生式集合PPstring UU,uu;//文法GG非终结符集合U,终结符集合uint nn,tt,kk;//消除左递归后的非终结符、终结符个数,产生式数string** table;//分析表cout<<" 欢迎使用LL(1)语法分析器!\n\n\n";cout<<"请输入文法(同一左部的规则在同一行输入,例如:E::=E+T|T;用^表示空串)\n"; input_grammer(G);preprocess(G,P,U,u,n,t,k);cout<<"\n该文法有"<<n<<"个非终结符:\n";for(i=0;i<n;i++) cout<<U[i];cout<<endl;cout<<"该文法有"<<t<<"个终结符:\n";for(i=0;i<t;i++) cout<<u[i];cout<<"\n\n 左递归检测与消除\n\n";if(eliminate_1(G,P,U,GG)){preprocess(GG,PP,UU,uu,nn,tt,kk);cout<<"该文法存在左递归!\n\n消除左递归后的文法:\n\n";for(i=0;i<nn;i++) cout<<GG[i]<<endl;cout<<endl;cout<<"新文法有"<<nn<<"个非终结符:\n";for(i=0;i<nn;i++) cout<<UU[i];cout<<endl;cout<<"新文法有"<<tt<<"个终结符:\n";for(i=0;i<tt;i++) cout<<uu[i];cout<<endl;//cout<<"新文法有"<<kk<<"个产生式:\n";//for(i=0;i<kk;i++) cout<<PP[i]<<endl;}else{cout<<"该文法不存在左递归\n";GG=G;PP=P;UU=U;uu=u;nn=n;tt=t;kk=k;}cout<<" 求解FIRST集\n\n";int *empty=ifempty(PP,UU,kk,nn);string* first=FIRST_X(PP,UU,uu,empty,kk,nn);for(i=0;i<nn;i++)cout<<"FIRST("<<UU[i]<<"): "<<first[i]<<endl;cout<<" 求解FOLLOW集\n\n";for(i=0;i<nn;i++)cout<<"FOLLOW("<<UU[i]<<"): "<<FOLLOW[i]<<endl; cout<<"\n\n 构造文法分析表\n\n";table=create_table(PP,UU,uu,nn,tt,kk,first);cout<<" ";for(i=0;i<tt;i++) cout<<" "<<uu[i]<<" ";cout<<"# "<<endl;for( i=0;i<nn;i++){cout<<UU[i]<<" ";for(j=0;j<t+1;j++)cout<<table[i][j];cout<<endl;}cout<<"\n\n 分析符号串\n\n";string s;cout<<"请输入要分析的符号串\n";cin>>s;analyse(table,UU,uu,tt,s);return 0;}3、程序演示结果(1)输入文法(2)消除左递归(3)求解FIRST和FOLLOW集(4)构造分析表(5)分析符号串匹配成功的情况:匹配失败的情况五、思考和体会1、编写的LL(1)语法分析器应该具有智能性,可以由用户输入任意文法,不需要指定终结符个数和非终结符个数。
LL(1)语法分析器实验总报告书
LL(1)语法分析器实验报告书小组负责人:潘豪 SWE08030小组成员:邹杰光 SWE08018叶凯翔 SWE08039张志峰 SWE08046许英俊 SWE08062厦门大学嘉庚学院计算机系软件工程班课程小组2010年7月目录第一章概述 (3)1.1 开发平台 (3)1.2 实验目的 (3)1.3 实验要求 (3)第二章语法分析器的实现 (3)2.1 LL(1)语法分析器原理 (3)2.2 求出能推出空的终结符 (4)2.3 FIRST集的确定 (4)2.4 FOLLOW集的确定 (4)2.5 SELECT集的确定 (4)2.6 LL(1)文法的判别 (4)2.7 逻辑结构 (4)2.8 预测分析表的生成 (5)2.9 句子的判定 (5)2.10 其他说明 (5)第三章系统运行与测试 (6)3.1 程序运行环境 (6)3.2 运行界面 (6)3.3 系统测试 (8)第四章总述 (11)4.1 程序综述 (11)4.2小组工作总结 (11)第一章概述1.1 开发平台本程序基于Microsoft Visual Studio 2008开发,使用C#语言。
1.2 实验目的掌握LL(1)分析法的基本原理,掌握LL(1)分析表的构造方法,掌握LL(1)驱动程序的构造方法。
1.3 实验要求编写一个语法分析器,不限语言,方法。
第二章语法分析器的实现2.1 LL(1)语法分析器原理语法分析是编译过程的核心部分,它的主要任务是按照程序的语法规则,从由词法分析输出的源程序符号串中识别出各类语法成分,同时进行词法检查,为语义分析和代码生成作准备。
这里采用自顶向下的LL(1)分析方法。
语法分析程序的流程图如图5-4所示。
语法分析程序流程图2.2 求出能推出空的终结符见许英俊报告2.3 FIRST集的确定见邹杰光报告2.4 FOLLOW集的确定见张志峰报告2.5 SELECT集的确定见叶凯翔报告2.6 LL(1)文法的判别见叶凯翔报告2.7 逻辑结构一个LL(1)分析器由一张分析表、一个分析栈和一个总控程序组成。
编译原理LL(1)分析器(C语言)
*
知
FOLLOW (T ) FOLLOW ( F )
,
即
FOLLOW(F)={*,+,),#}。 4 LL(1)分析表 知道了 FIRST 集和 FOLLOW 集, 我们就可以构造出 LL(1)分析表。 文法 G[E] 的分析表如表 1:
表1 i E E’ T T’ F + 文法 G[E]的分析表 * ( ) #
if(ch[l]!=cha) { if(ch[l]!=39) { switch(ch[l]) { case 'E': m=0;break; case 'T': m=2;break; case 'F': m=4;break; default: m=-1;break; } }/*if(ch[l]!=''')*/ else { switch(ch[l-1]) { case 'E': m=1;break; case 'T': m=3;break; default: m=-1;break; } }/*if(ch[l])=='''*/ }/*if ch[l]!=cha*/ if(m!=-1) { if(ch[l]!=cha) { how=ll1[m][j]; if(how==1) { printf("Pop %c,push E->TE' by reversing!\n",ch[l]); n=3; l=l+n-1; ch[l]='T'; ch[l-1]=39; ch[l-2]='E'; step=step+1; }/*if how==1*/
LL(1)语法分析实验报告
LL(1)语法分析实验报告一、实验目的通过设计、编制、调试一个典型的语法分析程序,实现对词法分析程序所提供的单词序列进行语法检查和结构分析,检查语法错误,进一步掌握常用的语法分析方法。
二、实验内容构造LL(1)语法分析程序,任意输入一个文法符号串,并判断它是否为文法的一个句子。
程序要求为该文法构造预测分析表,并按照预测分析算法对输入串进行语法分析,判别程序是否符合已知的语法规则,如果不符合则输出错误信息。
消除递归前的文法消除递归后的等价文法E→E+T E→TE’E→T E’→+TE’|εT→T*F T→FT’T→F T’→*FT’|εF→(E)|i F→(E)|i根据已建立的分析表,对下列输入串:i+i*i进行语法分析,判断其是否符合文法。
三、实验要求1.根据已由的文法规则建立LL(1)分析表;2.输出分析过程。
请输入待分析的字符串: i+i*i符号栈输入串所用产生式#E i+i*i# E→TE’#E’T i+i*i# T→FT’#E’T’F i+i*i# F→i#E’T’i i+i*i##E’T’ +i*i# T’→ε#E’ +i*i# E’→+TE’#E’T+ +i*i##E’T i*i# T→FT’#E’T’F i*i# F→i#E’T’i i*i##E’T’ *i# T’→*FT’#E’T’F* *i##E’T’F i# F→i#E’T’i i##E’T’ # T’→ε#E’ # E’→ε# #四、程序思路模块结构:1、定义部分:定义常量、变量、数据结构。
2、初始化:设立LL(1)分析表、初始化变量空间(包括堆栈、结构体等);3、运行程序:让程序分析一个text文件,判断输入的字符串是否符合文法定义的规则;4、利用LL(1)分析算法进行表达式处理:根据LL(1)分析表对表达式符号串进行堆栈(或其他)操作,输出分析结果,如果遇到错误则显示简单的错误提示。
五、程序流程图输入要分析的串判断输入串是否正确判断分析句型是否完全匹配?成功失败否是是否八、程序调试与测试结果运行后结果如下:九、实验心得递归下降分析法是确定的自上而下分析法,这种分析法要求文法是LL(1)文法。
编译原理-实验报告3-LL1
计算机硬件实验室实验报告根据某一文法编制调试LL(1)分析程序,以便对任意输入的符号串进行分析。
本次实验的目的主要是加深对预测分析LL(1)分析法的理解。
二、实验要求:对下列文法,用LL(1)分析法对任意输入的符号串进行分析:(1)E->TG(2)G->+TG|—TG(3)G->ε(4)T->FS(5)S->*FS|/FS(6)S->ε(7)F->(E)(8)F->i输出的格式如下:(1)LL(1)分析程序,编制人:姓名,学号,班级(2)输入一以#结束的符号串(包括+—*/()i#):在此位置输入符号串(3)输出过程如下:步骤分析栈剩余输入串所用产生式1 E i+i*i# E->TG(4)输入符号串为非法符号串(或者为合法符号串)备注:(1)在“所用产生式”一列中如果对应有推导则写出所用产生式;如果为匹配终结符则写明匹配的终结符;如分析异常出错则写为“分析出错”;若成功结束则写为“分析成功”。
(2) 在此位置输入符号串为用户自行输入的符号串。
(3)上述描述的输出过程只是其中一部分的。
注意:1.表达式中允许使用运算符(+-*/)、分割符(括号)、字符i,结束符#;2.如果遇到错误的表达式,应输出错误提示信息(该信息越详细越好);三、实验过程:1.模块设计:将程序分成合理的多个模块(函数),每个模块做具体的同一事情。
2.写出(画出)设计方案:模块关系简图、流程图、全局变量、函数接口等。
3.程序编写(1)定义部分:定义常量、变量、数据结构。
(2)初始化:设立LL(1)分析表、初始化变量空间(包括堆栈、结构体、数组、临时变量等);(3)控制部分:从键盘输入一个表达式符号串;(4)利用LL(1)分析算法进行表达式处理:根据LL(1)分析表对表达式符号串进行堆栈(或其他)操作,输出分析结果,如果遇到错误则显示错误信息。
四、实验结果:(1)写出程序流程图(2)给出运行结果示例程序:注意:本示例只要修改分析的句子即可,不要改写文法/*LL(1)分析法源程序,只能在VC++中运行*/#include<stdio.h>#include<stdlib.h>#include<string.h>#include<dos.h>char A[20];/*分析栈*/char B[20];/*剩余串*/char v1[20]={'i','+','-','*','/','(',')','#'};/*终结符*/char v2[20]={'E','G','T','S','F'};/*非终结符*/int j=0,b=0,top=0,l;/*L为输入串长度*/typedef struct type/*产生式类型定义*/{char origin;/*大写字符*/char array[5];/*产生式右边字符*/int length;/*字符个数*/}type;type e,t,g,g1,g2,s,s2,s1,f,f1;/*结构体变量*/type C[10][10];/*预测分析表*/void print()/*输出分析栈*/{int a;/*指针*/for(a=0;a<=top+1;a++)printf("%c",A[a]);printf("\t\t");}/*print*/void print1()/*输出剩余串*/{int j;for(j=0;j<b;j++)/*输出对齐符*/printf(" ");for(j=b;j<=l;j++)printf("%c",B[j]);printf("\t\t\t");}/*print1*/void main(){int m,n,k=0,flag=0,finish=0;char ch,x;type cha;/*用来接受C[m][n]*//*把文法产生式赋值结构体*/e.origin='E';strcpy(e.array,"TG");e.length=2;t.origin='T';strcpy(t.array,"FS");t.length=2;g.origin='G';strcpy(g.array,"+TG");g.length=3;g1.origin='G';strcpy(g1.array,"-TG");g1.length=3;g2.origin='G';g2.array[0]='^';g2.length=1;/////////////////////////////////////////////s.origin='S';strcpy(s.array,"*FS");s.length=3;s1.origin='S';strcpy(s1.array,"/FS");s1.length=3;s2.origin='S';s2.array[0]='^';s2.length=1;////////////////////////////////f.origin='F';strcpy(f.array,"(E)");f.length=3;f1.origin='F';f1.array[0]='i';f1.length=1;for(m=0;m<=4;m++)/*初始化分析表*/for(n=0;n<=5;n++)C[m][n].origin='N';/*全部赋为空*//*填充分析表*///char v1[20]={'i','+','-','*','/','(',')','#'};/*终结符*///char v2[20]={'E','G','T','S','F'};/*非终结符*/C[0][0]=e;C[0][5]=e;C[1][1]=g;C[1][2]=g1;C[1][3]=C[1][4]=C[1][7]=g2;C[2][0]=t;C[2][3]=t;C[2][5]=t;C[3][1]=C[3][2]=s2;C[3][3]=s;C[3][4]=s1;C[3][6]=C[3][7]=s2;C[4][0]=f1; C[4][5]=f;printf("LL(1)分析程序,编制人:武普泉,20号,1020562班\n");printf("输入一以#结束的符号串(包括+ - * / () i #):");do/*读入分析串*/{scanf("%c",&ch);if ((ch!='i') &&(ch!='+') &&(ch!='-')&&(ch!='*')&&(ch!='/')&&(ch!='(')&&(ch!=')')&&(ch!='#')) {printf("输入串中有非法字符\n");exit(1);}B[j]=ch;j++;}while(ch!='#');l=j;/*分析串长度*/ch=B[0];/*当前分析字符*/A[top]='#'; A[++top]='E';/*'#','E'进栈*/printf("步骤\t\t分析栈\t\t剩余字符\t\t所用产生式\n");do{x=A[top--];/*x为当前栈顶字符*/printf("%d",k++);printf("\t\t");for(j=0;j<=7;j++)/*判断是否为终结符*/if(x==v1[j]){flag=1;break;}if(flag==1)/*如果是终结符*/{if(x=='#'){finish=1;/*结束标记*/printf("acc!\n");/*接受*/getchar();getchar();exit(1);}/*if*/if(x==ch){print();print1();printf("%c匹配\n",ch);ch=B[++b];/*下一个输入字符*/flag=0;/*恢复标记*/}/*if*/else/*出错处理*/{print();print1();printf("%c出错\n",ch);/*输出出错终结符*/exit(1);}/*else*/}/*if*/else/*非终结符处理*/{for(j=0;j<=4;j++)if(x==v2[j]){m=j;/*行号*/break;}for(j=0;j<=7;j++)if(ch==v1[j]){n=j;/*列号*/break;}cha=C[m][n];if(cha.origin!='N')/*判断是否为空*/{print();print1();printf("%c->",cha.origin);/*输出产生式*/for(j=0;j<cha.length;j++)printf("%c",cha.array[j]);printf("\n");for(j=(cha.length-1);j>=0;j--)/*产生式逆序入栈*/A[++top]=cha.array[j];if(A[top]=='^')/*为空则不进栈*/top--;}/*if*/else/*出错处理*/{print();print1();printf("%c出错\n",x);/*输出出错非终结符*/exit(1);}/*else*/}/*else*/}while(finish==0);}/*main*。
编译原理,语法分析程序设计(LL(1)分析法)
编译原理,语法分析程序设计(LL(1)分析法)1.实验目得:掌握LL(1)分析法得基本原理,掌握LL(1)分析表得构造方法,掌握LL(1)驱动程序得构造方法。
2、实验要求:实现LR分析法(P147,例4、6)或预测分析法(P121,例4、3). 3、实验环境:一台配置为1G 得XP 操作系统得PC 机;Visual C++6、0、4、实验原理:编译程序得语法分析器以单词符号作为输入,分析单词符号串就是否形成符合语法规则得语法单位,如表达式、赋值、循环等,最后瞧就是否构成一个符合要求得程序,按该语言使用得语法规则分析检查每条语句就是否有正确得逻辑结构,程序就是最终得一个语法单位。
编译程序得语法规则可用上下文无关文法来刻画。
语法分析得方法分为两种:自上而下分析法与自下而上分析法.自上而下就就是从文法得开始符号出发,向下推导,推出句子.而自下而上分析法采用得就是移进归约法,基本思想就是:用一个寄存符号得先进后出栈,把输入符号一个一个地移进栈里,当栈顶形成某个产生式得一个候选式时,即把栈顶得这一部分归约成该产生式得左邻符号。
自顶向下带递归语法分析:1、首先对所以得生成式消除左递归、提取公共左因子2、在源程序里建立一个字符串数组,将所有得生成式都存在这个数组中. 3、给每个非终结符写一个带递归得匹配函数,其中起始符得函数写在main函数里。
这些函数对生成式右边从左向右扫描,若就是终结符直接进行匹配,匹配失败,则调用出错函数。
如果就是非终结符则调用相应得非终结符函数. 4、对输入得符号串进行扫描,从起始符得生成式开始。
如果匹配成功某个非终结符生成式右边得首个终结符,则将这个生成式输出。
匹配过程中,应该出现得非终结符没有出现,则出错处理。
5、软件设计与编程:对应源程序代码:#include <iostream、h>#include stdio、h#includ e〈stack>using namespace std;struct Node1 {charvn;charvt;char s[10]; }MAP[20];//存储分析预测表每个位置对应得终结符,非终结符,产生式int k;//用R代表E“,W代表T',e 代表空char start=’E';int len=8; charG[10][10]={"E-TR“,”R—+TR”,"R—e”,”T-FW",”W-〉*FW",”W-e”,”F-〉(E)","F—i"};//存储文法中得产生式char VN[6]={"E",’R",’T',"W",’F’};//存储非终结符char VT[6]={"i',’+’,"*’,"(’,’)’,'#'};//存储终结符charSELECT[10][10]={"(,i","+","),#","(,i”,"*","+,),#”,"(",”i"};//存储文法中每个产生式对应得SELECT集char Right[10][8]={"-〉TR”,"-+TR”,”-e”,”-〉FW","-*FW”,"—>e","-〉(E)”,"—stack 〈char stak;bool pare(char*a,char *b) {int i,la=strlen(a),j,lb=strlen(b);for(i=0;i〈la;i++) {)++j;bl0=j(rofﻩ{if(a[i]==b[j])return 1;}}return 0;}char*Find(char vn,char vt){inti;for(i=0;i<k;i++){if(MAP[i]、vn==vn &MAP[i]、vt==vt){};s、]i[PAM nruterﻩ} };”rorre" nruterﻩchar *Analyse(char * word) {char p,action[10],output;int i=1,j,l=strlen(word),k=0,l_act,m;while(!stak、empty()){stak、pop();}stak、push(’#’);;)trats(hsup、katsﻩprintf("___________________________________________________________\n”);printf("\n对符号串%s 得分析过程\n”,word);printf(” —-------—--——---——---—--——-——-—-—---—-—---———--—-——--—-—--—-—-——--————-\n");printf(”\n");p rintf ("步骤栈顶元素剩余输入串动作\n”);printf(” -—-———-——---—-—-—————----——----——-—--—--—--———-——-—-----————-—---—-—--—\n”);p=stak、top();while(p!=’#’){printf("%7d",i++);p=stak、top();stak、pop();printf(”%6c”,p);for(j=k,m=0;j<l;j++) {};]j[drow=]++m[tuptuoﻩﻩoutput[m]="\0';printf("%10s",output);if(p==word[k]){if(p=="#"){printf("分析成功\n”);return"SUCCESS";}printf(”匹配终结符%c\n”,p);k++;}else{strcpy(action,Find(p,word[k]));if(strcmp(action,”error")==0){printf("没有可用得产生式\n”);return ”ERROR"; }printf ("展开非终结符%c%s\n”,p,action);int l_act=strlen(action);if(action[l_act—1]=='e'){ };eunitnocﻩﻩfor(j=l_act—1;jj--){}}};)]j[noitca(hsup、katsﻩﻩ if(strcmp(output,"#")!=0)return "ERROR";}int main() {freopen("in、txt”,"r”,stdin);char source[100];inti,j,flag,l,m;printf("\n***为了方便编写程序,用R 代表E',W 代表T’,e 代表空*****\n\n”);printf("该文法得产生式如下:\n");for(i=0;i〈len;i++) {};)]i[G,"n\s%”(ftnirpﻩprintf("___________________________________________________________\n");printf("\n 该文法得SELECT集如下:\n");for(i=0;ilen;i++){printf("SELECT(%s)= { %s }\n",G[i],SELECT[i]);}ﻩprintf("___________________________________________________________\n");//判断就是否就是LL(1)文法flag=1;for(i=0;i〈8;i++){for(j=i+1;j〈8;j++){if(G[i]==G[j]){if(pare(SELECT[i],SELECT[j])){flag=0;ﻩﻩ} }};kaerbﻩ)8=!j(fiﻩ{break;}}if(flag){printf("\n有相同左部产生式得SELECT 集合得交集为空,所以文法就是LL(1)文法。
编译原理课程设计-LL(1)语法分析器的构造
LL(1)语法分析器的构造摘要语法分析的主要任务是接收词法分析程序识别出来的单词符由某种号串,判断它们是否语言的文法产生,即判断被识别的符号串是否为某语法部分。
一般语法分析常用自顶向下方法中的LL分析法,采用种方法时,语法分程序将按自左向右的顺序扫描输入的的符号串,并在此过程中产生一个句子的最左推导,即LL是指自左向右扫描,自左向右分析和匹配输入串。
经过分析,我们使用VC++作为前端开发工具,在分析语法成分时比较方便直观,更便于操作。
运行程序的同时不断修正改进程序,直至的到最优源程序。
关键字语法分析文法自顶向下分析 LL(1)分析最左推导AbstractGrammatical analysis of the main tasks was to receive lexical analysis procedure to identify the words from a website, string, and judge whether they have a grammar of the language, that is, judging by the series of symbols to identify whether a grammar part. General syntax analysis commonly used top-down methods of LL analysis, using methods, Grammar hours will be from the procedures of the order left-to-right scanning input string of symbols, and in the process produced one of the most left the sentence is derived, LL is scanned from left to right, From left to right analysis and matching input strings. After analysis, we use VC + + as a front-end development tool for the analysis of syntax ingredients more convenient visual, more easy to operate. Operational procedures at the same time constantly improving procedures, until the source of optimal .Key WordsGrammatical analysis grammar Top-down analysis LL (1) AnalysisMost left Derivation目录摘要 (1)引言 (3)第一章设计目的 (4)第二章设计的内容和要求 (5)2.1 设计内容 (5)2.2 设计要求 (5)2.3 设计实现的功能 (5)第三章设计任务的组织和分工 (6)3.1 小组的任务分工 (6)3.2 本人主要工作 (6)第四章系统设计 (9)4.1 总体设计 (9)4.2 详细设计 (9)第五章运行与测试结果 (22)5.1 一组测试数据 (22)5.2 界面实现情况 (23)第六章结论 (27)课程设计心得 (28)参考文献 (29)致谢 (30)附录(核心代码清单) (31)引言编译器的构造工具是根据用户输入的语言的文法,编译器的构造工具可以生成程序来处理以用户输入的文法书写的文本。
实验三 LL(1)语法分析程序的构造
LL(1)语法分析器的构造实验目的1 掌握LL(1)分析法的基本原理2 掌握LL(1)分析表的构造方法3 掌握LL(1)驱动程序的构造方法实验任务已知文法G[E]:E→E+T|TT→T*F|FF→(E)|i说明:终结符号i为用户定义的简单变量, 即标识符的定义。
1、消除文法的左递归,构造对应文法的预测分析表;2、根据构造的预测分析表,实现LL(1)分析中控制程序(表驱动程序),并完成整个的LL(1)分析程序的界面设计、运行;3.P104中,3.36写一个Yacc程序,把输入的算术表达式翻译成对应的后缀表达式输出。
要求转换正确,同时对于简单错误能够识别。
4.P104中,3.37,写一个Yacc“台式计算器”程序,它计算布尔表达式,其中的词法分析器用Lex写。
要求转换正确,同时对于简单错误能够识别。
实验学时6学时实验要求1、输入串应是词法分析的输出二元式序列,即某算术表达式“实验项目一”的输出结果。
输出为输入串是否为该文法定义的算术表达式的判断结果。
2、LL(1)分析过程应能发现输入串中的错误。
3、设计至少两个测试用例(尽可能完备,正确和出错),并给出测试结果。
实验原理1. 对文法要求LL(1)分析法属于自顶向下分析方法,因此需要预测匹配的产生式。
即在LL(1)分析法中,每当在符号栈的栈顶出现非终结符时,要预测用哪个产生式的右部去替换该非终结符。
LL(1)分析方法要求文法满足如下条件:对于任一非终结符A,其任意两个产生式A→α,A→β,都要满足下面条件:First(A→α)∩First(A→β)=∅2. 分析表构造LL(1)分析表的作用是对当前非终结符和输入符号确定应该选择用哪个产生式进行推导。
它的行对应文法的非终结符,列对应终结符,表中的值有两种:一是产生式的编号,一是错误编号。
若用T表示LL(1)分析表,则T可表示如下:T: V N×V T→P∪{Error}T(A, t) = A→α,当t∈First(A→α)T(A, t) = Error,否则其中P表示所有产生式的集合。
编译原理实验 LL(1)分析
实验三语法分析---LL(1)分析器目录一. 实验的目的与思路 (2)(1)目的 (2)(2)思路 (2)二. 基本的功能 (3)三.总体设计 (4)四.详细设计 (5)五.源程序清单 (6)六.源代码 (6)....................................................................................................................................................一. 实验的目的与思路(1)目的1. 用程序的方法实现语法分析的LL(1)方法。
(2)思路本程序是采用的LL(1)方法进行的语法分析,而LL(1)的分析方法是属于自上而下的方法。
自上而下分析的基本思想是:对任意输入串,试图用一切可能的方法,从文法开始符号(根结点)出发,自上而下为输入串建立一棵语法树。
从推导的角度看,它是从文法的开始符号出发,反复使用各种产生式,寻找与输入串匹配的推导。
在输入之前必须要进行该文法是不是LL(1)文法的判断,然后再构造相应的LL(1)分析表来用预测分析方法进行语法分析,依据下面的文法及分析表来设计程序实现预测分析的分析过程。
1.文法G[E]:E --> E+T|TT --> T*F|FF --> (E) | i2.通过观察可知道该文法是一个左递归文法,要进行语法分析就必须消除左递归才能继续判断它是不是LL(1)文法,然后才能用预测分析方法进行语法分析。
3.消除左递归:E -->TMM --> +TM|uT --> FQQ --> *FQ|uF --> (E) | i4.在进行LL(1)文法的判断:(1.)各非终结符的FIRST集如下:FIRST(E)= FIRST(TM)= {(,i }FIRST(M)= { + }FIRST(T)= {(,i }FIRST(Q)= { * }FIRST(F)= { (,i }(2.)各非终结符的FOLLOW集如下:FOLLOW(E)= { ),# }FOLLOW(M)= { ),# }FOLLOW(T)= { + ,),# }FOLLOW(Q)= { + ,),# }FOLLOW(F)= { * ,+ ,),# }(3.)各产生式的SELECT集如下:SELECT(E→TM)={ ( ,i }SELECT(M→+TM)={+}SELECT(M→u)={ }, #}SELECT(T→FQ)={ ( ,i }SELECT(Q→*FQ)={ *}SELECT(Q→u)={ +,), # }SELECT(F→(E))={ ( )SELECT(F→i)={ i }因为:SELECT(M→+TM)∩SELECT(M→u)=ФSELECT(Q→*FQ)∩SELECT(Q→u)=ФSELECT(F→(E))∩SELECT(F→i)=Ф因此可以判断该文法是一个LL(1)文法可以构造预测分析表。
编译原理课件-LL(1)文法及其分析程序
例 5.3
若有文法G3[S]: S → aA|d A →bAS|ε 識別輸入串w=abd是否是G3[S]的句子 試探推導出abd的推導過程為: S => aA => abAS => abS => abd 試探推導成功。
1
文法G3[S]的特點:
文法中含有空產生式。
由此可以看出,當某一非終結符的產生式 中含有空產生式時,它的非空產生式右部的 首符號集兩兩不相交,並與在推導過程中緊 跟該非終結符後邊可能出現的終結符集也不 相交,則仍可構造確定的自頂向下分析。
1
用產生式(1)、(2)的右部代替產生式(3)中
的非終結符A得到左部為B的產生式為:
(1) B→aBc
(1) A→aB
(2) B→Bbc (3) B→d
(2) A→Bb (3) B→Ac (4) B→d
消除左遞歸後得:
B→(aBc|d)B′ B′→bcB′|ε 再把原來其餘的產生式A→aB,
A→Bb加入,最終文法為:
1
語法樹-推導的幾何表示
句型aabbaa的可能推導序列和語法樹
例: G[S]: S→aAS A→SbA A→SS S→a A→ba
S
aA
S
S bAa
a
ba
SaASaAaaSbAaaSbbaaaabbaa
SaASaSbASaabASaabbaSaabbaa
SaASaSbASaSbAaaabAaaabbaa
用Ai-->1| 2r…| k r替代 形如Ai--> Ajr的規則,其中 Aj--> 1| 2…| k是關於Aj的全部產生式;
消除Ai規則的直接左遞歸;
end;
(3)化簡由2得到的文法
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
集美大学计算机工程学院实验报告课程名称:编译原理指导教师:付永钢实验成绩:实验编号:实验三实验名称:LL(1)语法分析器的构造班级:计算12姓名:学号:上机实践日期:2014.12上机实践时间:6学时一、实验目的1、掌握LL(1)分析法的基本原理;2、掌握LL(1)分析表的构造方法;3、掌握LL(1)驱动程序的构造方法。
二、实验环境Windows7 x64、VC6.0三、实验原理1、对文法要求LL(1)分析法属于自顶向下分析方法,因此需要预测匹配的产生式。
即在LL(1)分析法中,每当在符号栈的栈顶出现非终结符时,要预测用哪个产生式的右部去替换该非终结符。
LL(1)分析方法要求文法满足如下条件:对于任一非终结符A,其任意两个产生式A→α,A→β,都要满足下面条件:First(A→α)∩First(A→β)=∅2、分析表构造LL(1)分析表的作用是对当前非终结符和输入符号确定应该选择用哪个产生式进行推导。
它的行对应文法的非终结符,列对应终结符,表中的值有两种:一是产生式的编号,一是错误编号。
若用T表示LL(1)分析表,则T可表示如下:T: V N×V T→P∪{Error}T(A, t) = A→α,当t∈First(A→α)T(A, t) = Error,否则其中P表示所有产生式的集合。
显然,一个文法G是LL(1)文法,当且仅当T的元素包含唯一的一个产生式或Error。
3、驱动程序构造LL(1)分析主要包括以下四个动作,其中X为符号栈栈顶元素,a为输入流当前字符。
●替换:当X∈V N时选相应产生式的右部β去替换X。
●匹配:当X∈V T时它与a进行匹配,其结果可能成功,也可能失败,如果成功则符号栈中将X退栈并将输入流指针向前移动一位,否则报错。
●成功:当格局为(空,空)时报告分析成功。
●报错:出错后,停止分析。
四、实验内容已知文法G[E]:E→E+T|TT→T*F|FF→(E)|i说明:终结符号i为用户定义的简单变量, 即标识符的定义。
1、消除文法的左递归,构造对应文法的预测分析表;2、根据构造的预测分析表,实现LL(1)分析中控制程序(表驱动程序),并完成整个的LL(1)分析程序的界面设计、运行;3、P104中,3.36写一个Yacc程序,把输入的算术表达式翻译成对应的后缀表达式输出。
要求转换正确,同时对于简单错误能够识别。
4、P104中,3.37,写一个Yacc“台式计算器”程序,它计算布尔表达式,其中的词法分析器用Lex写。
要求转换正确,同时对于简单错误能够识别。
五、实验要求1、输入串应是词法分析的输出二元式序列,即某算术表达式“实验项目一”的输出结果。
输出为输入串是否为该文法定义的算术表达式的判断结果。
2、LL(1)分析过程应能发现输入串中的错误。
3、设计至少两个测试用例(尽可能完备,正确和出错),并给出测试结果。
六、实验步骤、1、分析文法(1)E=>E+T=>E+T*F=>E+T*(E)即有E=>E+T*(E)存在左递归。
用直接改写法消除左递归,得到如下文法:G[E]:E→TE’E’→+TE’|εT→FT’T’→*FT’|εF→(E)|i(2)对于以上改进的文法,可以得到:FIRST(E’)=FIRST(+TE’)∪FIRST(-TE’)∪{ε}={+,ε}FIRST(T’)=FIRST(*FT’)∪FIRST(/FT’)∪{ε}={*,ε}FIRST(E)= FIRST( T ) = FIRST( F )=FIRST((E))∪FIRST(i)={(,i }由此得到各非终结符的FOLLOW集合:FOLLOW(E)={ ),$}FOLLOW(E’)=FOLLOW(E)={),$}FOLLOW(T)=FIRST(E’)∪FOLLOW(E’)={ +,),$}FOLLOW(T’)=FOLLOW(T)={ +,),$}FOLLOW(F)=FIRST1、构造LL(1)分析表采用手工操作构造LL(1)分析表。
LL(1)分析表用一个二维矩阵表示,其中每个非终结符对应一行,每个终结符对应一列,一个非终结符和一个终结符可以确定矩阵中的一个元素,元素的值表示该非终结符和该终结符对应的产生式。
每个矩阵元素都是一个符号串,所有元素初始化为””;构造LL(1)表时,根据文法和各个产生式的First集,填写LL(1)分析表的内容。
各产生式只要降右端填入到对应的表项中即可,用空串表示Error。
在根据LL(1)分析表选择产生式进行推导时,若查到的产生式为空串表示无相应的产生式可选,不匹配错误;否则根据符号串得到相应的产生式进行推导,即进行压栈2.数据结构LL(1)语法分析程序共用到个栈,分别称为:符号栈,语法树栈,操作符栈和操作数栈。
其中,符号栈用于进行LL(1)语法分析;其它的栈是为了在语法分析的过程中同时生成与源程序结构对应的语法树而设。
语法树栈用于生成声明部分和语句部分的语法树;操作符栈和操作数栈用于生成表达式部分的语法树。
3. 构造驱动程序构造LL(1)驱动程序的算法:(1). 分析开始时,首先将标志符号#和文法开始符号S依次压入符号栈;输入流指针指向第一个输入符号,即由符号栈和输入流构成的初始格局为:(#S, a1a2...a n#)然后,反复执行第2步所列的工作。
(2). 设在分析的某一步,符号栈及剩余的输入流处于如下的格局(#X1X2...X m-1X m, a i a i+1...a n#)其中,X1X2...X m-1X m为分析过程中所得的文法符号,此时,可视栈顶符号X m的不同情况,分别作如下动作:●若X m∈V N,则以X m及a i组成的符号对(X m,a i)查分析表T。
设T(X m,a i)为一产生式,假设是X m→UVW,此时将X m从分析栈中退出,并将UVW压入栈中,从而得到新的格局(#X1X2...X m-1WVU, a i a i+1...a n#)但若T(X m,a i)=Error,则调用出错处理程序进行处理;●若X m=a i≠#,则表明栈顶符号已经与当前扫描的输入符号得到匹配,此时应将X m(即a i)从栈中退出,并将输入流指针向前移动一个位置。
●若X m=a i=#,则表明输入串已经完全得到匹配,此时即可宣告分析成功而结束分析。
其它情形,转错误处理程序。
程序见附录七、实验结果六、实验小结1、在本次实验中,通过设计、编制、调试一个递归下降语法分析程序,实现对词法分析程序所提供的单词序列进行语法检查和结构分析,掌握LL(1)分析法的基本原理、掌握LL(1)分析表的构造方法、掌握LL(1)驱动程序的构造方法;2、通过本次实验,对LL(1)递归下降分析程序的构造和设计有了更为全面的认识,对LL(1)文法分析程序的实现有了进一步了解;3、实验输入串以‘$’结束,才用栈的形式,依次弹出进行处理;附录:程序代码:import java.io.IOException;import java.util.Scanner;import java.util.Stack;public class test3{static char[] a=new char[20];static int n=0,i=0;static char c;static String s;static Stack<Character> sk = new Stack<Character>();public static void main(String[] args){System.out.println("input:");Scanner in = new Scanner(System.in);char z = 0;try {z = (char)System.in.read();} catch (IOException e) {e.printStackTrace();}while(z!='$'){a[n]=z;try {z=(char)System.in.read();} catch (IOException e) {e.printStackTrace();}n++;a[n]='$';}sk.push('$');sk.push('E');c=sk.pop();do{if(c==a[i]){ i++;c=sk.pop();}else if(a[i]=='$') break;else ll1();}while(c!='$');}private static void ll1() {if(c=='E'){if(a[i]=='i'||a[i]=='('){System.out.println("E->TE'");sk.push('e');sk.push('T');c=sk.pop();}else error();}else if(c=='e'){if(a[i]=='+'){System.out.println("E'->+TE'");sk.push('e');sk.push('T');c=sk.pop();}else if(a[i]==')'||a[i]=='$'){System.out.println("E'->&");c=sk.pop();}else error();}else if(c=='T'){if(a[i]=='i'||a[i]=='('){System.out.println("T->FT'");sk.push('t');sk.push('F');c=sk.pop();}else error();}else if(c=='t'){if(a[i]=='+'||a[i]==')'||a[i]=='$'){System.out.println("T'->&");c=sk.pop();}else if(a[i]=='*'){System.out.println("T'->*FT'");sk.push('t');sk.push('F');sk.push('*');c=sk.pop();}else error();}else if(c=='F'){if(a[i]=='i'){System.out.println("F->i");sk.push('i');c=sk.pop();}else if(a[i]=='('){System.out.println("F->(E)");sk.push(')');sk.push('(');c=sk.pop();}else error();}}private static void error() {System.out.println((i+1)+":"+a[i]+" error!");i++;}}。