lR语法分析器设计
《编译原理》中LR(0)语法分析动态演示系统分析与设计

《编译原理》中LR(0)语法分析动态演示系统分析与设计1. 引言1.1 研究背景编译原理是计算机科学领域的重要基础课程,而LR(0)语法分析是编译原理中一个关键的内容。
LR(0)语法分析是一种自底向上的语法分析方法,能够准确地判断一个输入串是否是给定文法的句子,同时可以生成句子对应的语法树。
LR(0)语法分析比上下文无关文法分析更为强大,因此被广泛应用于编译器的设计和实现中。
对于学习者来说,理解和掌握LR(0)语法分析并不是一件容易的事情。
传统的教学方法往往是通过讲解和演示来进行,但存在一定的局限性,学生很难深入理解其中的逻辑和原理。
设计一个LR(0)语法分析动态演示系统是十分必要和有意义的。
这样的系统可以通过图形化的界面展示LR(0)语法分析的每个步骤和过程,帮助学生更直观地理解LR(0)语法分析的原理和实现。
1.2 研究目的研究目的是为了通过设计和实现一个LR(0)语法分析动态演示系统,帮助学生和从业者更好地理解和应用LR(0)语法分析算法。
具体来说,研究目的包括但不限于以下几点:通过分析LR(0)语法分析算法的原理和流程,深入探讨其在编译原理中的重要性和应用价值,为用户提供一个直观、动态的学习工具,帮助他们更好地理解和掌握这一算法的核心概念。
通过设计和实现一个功能齐全、易于操作的LR(0)语法分析动态演示系统,提供用户友好的界面和交互功能,使用户可以通过实际操作和观察,加深对LR(0)语法分析算法的认识,并在实践中掌握其使用方法和技巧。
通过系统测试和优化,不断改进系统性能和用户体验,确保系统稳定运行并具有良好的可用性和可靠性,为用户提供一个高质量的学习工具和应用工具。
通过这些努力,旨在提高用户对LR(0)语法分析算法的理解和应用能力,促进编译原理领域的教学和研究工作的发展。
1.3 研究意义编译原理是计算机专业的重要基础课程,而LR(0)语法分析是编译原理中一项重要的内容。
通过设计和实现一个LR(0)语法分析动态演示系统,可以帮助学生更加直观地理解和掌握LR(0)语法分析的原理和算法。
《编译原理》中LR(0)语法分析动态演示系统分析与设计

《编译原理》中LR(0)语法分析动态演示系统分析与设计一、引言随着计算机科学领域的不断发展,编译原理作为计算机科学的核心课程之一,对于理解编程语言和编译器的原理和技术具有重要意义。
LR(0)语法分析作为编译器中的重要组成部分,在编译原理课程中具有重要的地位。
为了更好地理解与掌握LR(0)语法分析,本文将课程中的相关理论与实践相结合,提出了一个LR(0)语法分析动态演示系统的设计与分析。
二、LR(0)语法分析概述在编译器的构建中,语法分析是非常重要的一环。
而LR(0)语法分析是一种常用的语法分析方法之一。
LR(0)语法分析使用的是LR(0)自动机进行分析,它是一种自底向上的语法分析方法,它的优势在于可以处理大部分的上下文无关文法(Context-Free Grammar, CFG),并且可以准确地进行语法分析。
LR(0)语法分析是通过构建状态机的方式,根据文法产生式中的右部项目来进行状态转换并最终得到文法的推导序列。
为了更好地理解LR(0)语法分析的原理与过程,我们需要深入学习LR(0)自动机的构建过程、状态转换的规则以及分析过程的具体步骤。
仅仅通过理论学习,学生们往往难以深刻理解LR(0)语法分析的工作原理与流程。
我们需要设计一个能够直观演示LR(0)语法分析过程的系统,通过动态的展示来帮助学生更好地理解LR(0)语法分析的过程和原理。
三、LR(0)语法分析动态演示系统的需求分析为了实现LR(0)语法分析动态演示系统,首先需要进行系统需求分析,明确系统的功能需要和用户需求。
根据LR(0)语法分析的原理与过程,系统的主要功能需求包括:1. 文法输入:能够接受用户输入的文法表达式,包括非终结符、终结符及产生式。
2. LR(0)自动机构建:根据用户输入的文法表达式自动生成LR(0)自动机,并进行展示。
3. 状态转换展示:根据LR(0)自动机中的状态转换规则,动态展示状态之间的转换过程及转换规则。
4. 分析过程展示:根据LR(0)自动机和输入的句子,展示分析过程中状态的变化和产生式的规约过程。
语法分析程序的设计与实现

◆词法分析 用户必须提供一个词法分析器来读取输入流并把记号(带有值, 如果需要的话)传达到解析器。词法分析器使叫做 yylex 的整数值的 函数。这个函数返回一个整数的记号编号,它表示读取的记号的种类。 如果这个记号关联着一个值,应当把它赋予外部变量 yylval。 为使通信得以发生,解析器和词法分析器必须在记号编号上达成 一致。编号可以由 Yacc 或用户来选择。在这两种情况下,使用 C 语 言的“# define”机制允许词法分析器使用符号来返回这些编号。例如, 假定在 Yacc 规定文件的声明段中已经定义记号名字 DIGIT。 它的意图是返回一个 DIGIT 记号编号,和等于这个数字的数值 的一个值。倘若词法分析器代码位于规定文件的程序段,标识符 DIGIT 将被定义为与记号 DIGIT 关联的记号编号。 这种机制导致清晰的、易于修改的词法分析器;唯一的缺点是在 文法中需要避免使用任何在 C 语言或解析器中保留的或有意义的记 号名字;例如,使用记号名字 if 或 while 就一定会导致编译词法分 析器时出现严峻的困难。记号名字 error 保留给错误处理,不应该随 便使用。 同上所述,记号编号可以由 Yacc 或用户来选择。在缺省的条件 下,编号由 Yacc 选择。文字字符的缺省记号编号是它在本地字符集 中的字符数值。其他名字赋予从 257 开始的记号编号。 要把一个记号编号赋予一个记号(包括文字),可以在声明段中记 号或文字的第一次出现时直接跟随着一个非负整数。这个整数被接受
第四:YACC 内部名称: ................................................................................................ 7 第五:运行结果(源代码见附录).............................................................................. 8 第六:实验总结 ............................................................................................................... 8 第七:附录 ..................................................................................................................... 10
LR语法分析器的实现代码(python)

LR语法分析器的实现代码(python)•构造LR(0)项目集:–构造I的闭包CLOSURE(I)的算法如下:i.I的任何项目都属于CLOSURE(I);ii.若A→α•Bβ属于CLOSURE(I),对任何产生式B→γ,B→•γ也属于CLOSURE(I);iii.重复执行上述两步骤直至CLOSURE(I)不再增大为止。
iv.实现代码如下def get_CLOSURE(tmp): # 生成闭包 CLOSURE = [] for it in tmp:if(it not in CLOSURE): CLOSURE.append(it) x, y = it.split(".") if(y == ""): continue v = y[0] if(v in VN): res = get_VN_gram(v) # 返回非终结符产生的A->.aBb形式 forre in res: if(re not in CLOSURE): CLOSURE.append(re) return CLOSURE–Go(I,a)函数构造算法i.I为当前状态,X为文法符号,J为I中所有形如A->α·Xβ的项目的后续项目所组成的集合,而CLOSURE(J)就是项目集I关于X的后续状态ii.实现代码如下def go(item, v): #生成并返回下一个item tmp = [] for it in item: x, y = it.split(".") if(y!=""): if(y[0] == v): new_it = x + y[0] + "." + y[1:] tmp.append(new_it) if(len(tmp)!=0): new_item = get_CLOSURE(tmp) #print(tmp) #print("go(item, "+v + ") = " + str(new_item)) return new_item–判别LR项目集是否合法:•无移进项目和规约项目并存•无多个规约项目并存•代码如下:def lr_is_legal(: # 判别lr是否合法 has_protocol = 0 #是否存在规约项目 has_shift = 0 #是否存在移进项目 for item in items: for it in item: x, y = it.split(".") if(y ==""): if(has_protocol != 0 or has_shift != 0): return False has_protocol = 1 else: if(y[0] in VT): has_shift = 1 return True•构造LR(0)分析表–构造算法:i.假定项目集规范族C={I0,I1,…,In}。
编译原理-语法分析程序报告

编译原理实验实验二语法分析器实验二:语法分析实验一、实验目的根据给出的文法编制LR(1)分析程序,以便对任意输入的符号串进行分析。
本次实验的目的主要是加深对LR(1)分析法的理解。
二、实验预习提示1、LR(1)分析法的功能LR(1)分析法的功能是利用LR(1)分析表,对输入符号串自下而上的分析过程。
2、LR(1)分析表的构造及分析过程。
三、实验内容对已给语言文法,构造LR(1)分析表,编制语法分析程序,要求将错误信息输出到语法错误文件中,并输出分析句子的过程(显示栈的内容);实验报告必须包括设计的思路,以及测试报告(输入测试例子,输出结果)。
语法分析器一、功能描述:语法分析器,顾名思义是用来分析语法的。
程序对给定源代码先进行词法分析,再根据给定文法,判断正确性。
此次所写程序是以词法分析器为基础编写的,由于代码量的关系,我们只考虑以下输入为合法:数字自定义变量+ * ()$作为句尾结束符。
其它符号都判定为非法。
二、程序结构描述:词法分析器:class wordtree;类,内容为字典树的创建,插入和搜索。
char gettype(char ch):类型处理代入字串首字母ch,分析字串类型后完整读入字串,输出分析结果。
因读取过程会多读入一个字母,所以函数返回该字母进行下一次分析。
bool isnumber(char str[]):判断是否数字代入完整“数字串”str,判断是否合法数字,若为真返回1,否则返回0。
bool isoperator(char str[]):判断是否关键字代入完整“关键字串”str,搜索字典树判断是否存在,若为存在返回1,否则返回0。
语法分析器:int action(int a,char b):代入当前状态和待插入字符,查找转移状态或归约。
node2 go(int a):代入当前状态,返回归约结果和长度。
void printstack():打印栈。
int push(char b):将符号b插入栈中,并进行归约。
实验5---语法分析器(自下而上):LR(1)分析法

实验5---语法分析器(自下而上):LR(1)分析法一、实验目的构造LR(1)分析程序,利用它进行语法分析,判断给出的符号串是否为该文法识别的句子,了解LR(K)分析方法是严格的从左向右扫描,和自底向上的语法分析方法。
二、实验内容程序输入/输出示例(以下仅供参考):对下列文法,用LR(1)分析法对任意输入的符号串进行分析:(1)E->E+T(2)E->E—T(3)T->T*F(4)T->T/F(5)F-> (E)(6)F->i输出的格式如下:(1)LR(1)分析程序,编制人:姓名,学号,班级(2)输入一个以#结束的符号串(包括+—*/()i#):在此位置输入符号串(3)输出过程如下:3.对学有余力的同学,测试用的表达式事先放在文本文件中,一行存放一个表达式,同时以分号分割。
同时将预期的输出结果写在另一个文本文件中,以便和输出进行对照。
三、实验方法1.实验采用C++程序语言进行设计,文法写入程序中,用户可以自定义输入语句;2.实验开发工具为DEV C++。
四、实验步骤1.定义LR(1)分析法实验设计思想及算法①若ACTION[sm , ai] = s则将s移进状态栈,并把输入符号加入符号栈,则三元式变成为:(s0s1…sm s , #X1X2…Xm ai , ai+1…an#);②若ACTION[sm , ai] = rj则将第j个产生式A->β进行归约。
此时三元式变为(s0s1…sm-r s , #X1X2…Xm-rA , aiai+1…an#);③若ACTION[sm , ai]为“接收”,则三元式不再变化,变化过程终止,宣布分析成功;④若ACTION[sm , ai]为“报错”,则三元式的变化过程终止,报告错误。
2.定义语法构造的代码,与主代码分离,写为头文件LR.h。
3.编写主程序利用上文描述算法实现本实验要求。
五、实验结果1. 实验文法为程序既定的文法,写在头文件LR.h中,运行程序,用户可以自由输入测试语句。
语法分析器实验报告

词法分析器实验报告实验名称:语法分析器实验内容:利用LL(1)或LR(1)分析语句语法,判断其是否符合可识别语法。
学会根据状态变化、first、follow或归约转移思想构造状态分析表,利用堆栈对当前内容进行有效判断实验设计:1.实现功能可对一段包含加减乘除括号的赋值语句进行语法分析,其必须以$为终结符,语句间以;隔离,判断其是否符合语法规则,依次输出判断过程中所用到的产生式,并输出最终结论,若有错误可以报错并提示错误所在行数及原因2.实验步骤3.算法与数据结构a)LLtable:left记录产生式左端字符;right记录产生式右端字符;ln记录产生式右端字符长度Status:记录token分析情况Token:category,类型;value,具体内容b)根据LL(1)算法,手工构造分析表,并将内容用数组存储,便于查找c)先将当前语句的各token按序存储,当前处理语句最后一个token以#标记,作为输入流与产生式比较,堆栈中初始放入#,x,a为处理输入流中当前读头内容✓若top=a=‘#‘表示识别成功,退出分析程序✓若top=a!=‘#‘表示匹配,弹出栈顶符号,读头前进一个✓若top为i或n,但top!=a,出错,输出当前语句所在行,出错具体字符✓若top不为i或n,查预测分析表,若其中存放关于top产生式,则弹出top,将产生式右部自右向左压入栈内,输出该产生式,若其中没有产生式,出错,输出当前语句所在行,出错具体字符d)以;作为语句终结,每次遇到分号则处理之前语句并清空后预备下语句处理,当遇到$表示该段程序结束,停止继续处理4.分析表构造过程a)x->i=ee->e+t|e-t|tt->t*f|t/f|ff->(e)|i|nnote: i表示变量,n表示数字,!表示空串b)提取左公因子x->i=ee->ea|ta->+t|-tt->tb|fb->*f|/ff->(e)|i|nc)消除左递归x->i=ee->tcc->ac|!a->+t|-tt->fdd->bd|!b->*e|/ff->(e)|i|n5.类class parser{public:LLtable table[100][100]; //LL(1)表void scanner(); //扫描输入流中内容并分析parser(istream& in); //初始化,得到输入文件地址int getLine() const; //得到当前行数private:int match(); //分析语法stack <char> proStack; //分析堆栈void constructTable(); //建立LL(1)表int getRow(char ch); //取字符所在表中行int getCol(char ch); //取字符所在表中列istream* pstream; //输入流void insertToken(token& t); //插入当前tokenstatus getToken(token& t); //找到tokenint getChar(); //得到当前字符int peekChar(); //下一个字符void putBackChar(char ch); //将字符放回void skipChar(); //跳过当前字符void initialization(); //初始化堆栈等int line; //当前行数token tokens[1000]; //字符表int counter; //记录当前字符表使用范围}6.主要代码void parser::constructTable() //建立LL(1)表{for (int i=0;i<8;i++){for (int j=0;j<9;j++){table[i][j].left=' ';for (int k=0;k<3;k++)table[i][j].right[k]=' ';}}table[0][6].left='x';table[0][6].ln=3;table[0][6].right[0]='i';table[0][6].right[1]='=';table[0][6].right[2]='e';table[1][4].left='e';table[1][4].ln=2;table[1][4].right[0]='t';table[1][4].right[1]='c';table[1][6].left='e';table[1][6].ln=2;table[1][6].right[0]='t';table[1][6].right[1]='c';table[1][7].left='e';table[1][7].ln=2;table[1][7].right[0]='t';table[1][7].right[1]='c';table[2][0].left='c';table[2][0].ln=2;table[2][0].right[0]='a';table[2][0].right[1]='c';table[2][1].left='c';table[2][1].ln=2;table[2][1].right[0]='a';table[2][1].right[1]='c';table[2][5].left='c';table[2][5].ln=0;table[2][5].right[0]='!';table[2][8].left='c';table[2][8].ln=0;table[2][8].right[0]='!';table[3][0].left='a';table[3][0].ln=2;table[3][0].right[0]='+'; table[3][0].right[1]='t'; table[3][1].left='a';table[3][1].ln=2;table[3][1].right[0]='-'; table[3][1].right[1]='t'; table[4][4].left='t';table[4][4].ln=2;table[4][4].right[0]='f'; table[4][4].right[1]='d'; table[4][6].left='t';table[4][6].ln=2;table[4][6].right[0]='f'; table[4][6].right[1]='d'; table[4][7].left='t';table[4][7].ln=2;table[4][7].right[0]='f'; table[4][7].right[1]='d'; table[5][0].left='d';table[5][0].ln=0;table[5][0].right[0]='!'; table[5][1].left='d';table[5][1].ln=0;table[5][1].right[0]='!'; table[5][2].left='d';table[5][2].ln=2;table[5][2].right[0]='b'; table[5][2].right[1]='d'; table[5][3].left='d';table[5][3].ln=2;table[5][3].right[0]='b'; table[5][3].right[1]='d'; table[5][5].left='d';table[5][5].ln=0;table[5][5].right[0]='!'; table[5][8].left='d';table[5][8].ln=0;table[5][8].right[0]='!'; table[6][2].left='b';table[6][2].ln=2;table[6][2].right[0]='*'; table[6][2].right[1]='f'; table[6][3].left='b';table[6][3].ln=2;table[6][3].right[0]='/'; table[6][3].right[1]='f'; table[7][4].left='f';table[7][4].ln=3;table[7][4].right[0]='(';table[7][4].right[1]='e';table[7][4].right[2]=')';table[7][6].left='f';table[7][6].ln=1;table[7][6].right[0]='i';table[7][7].left='f';table[7][7].ln=1;table[7][7].right[0]='n';}int parser::match() //分析语法{ofstream ofs("out.txt",ios::app);char a;int i=0;for (int p=0;p<counter;p++){cout<<tokens[p].value;ofs<<tokens[p].value;}cout<<endl;ofs<<endl<<"ANALYSIS:"<<endl;while(1){if(tokens[i].category=='n' || tokens[i].category=='i')a=tokens[i].category;elsea=(tokens[i].value)[0];if(a==proStack.top()){if(a=='#'){cout<<"This is valid!"<<endl<<endl;ofs<<"This is valid!"<<endl<<endl;return 0;}else{proStack.pop();i++;}}else{if(proStack.top() =='n'|| proStack.top() =='i'){if(a!='#'){cout<<"ERROR(LINE "<<getLine()<<" ): "<<a<<" cannot be matched"<<endl;ofs<<"ERROR(LINE "<<getLine()<<" ): "<<a<<" cannot be matched"<<endl;}else{cout<<"ERROR(LINE "<<getLine()<<" ): Unexpected ending"<<endl;ofs<<"ERROR(LINE "<<getLine()<<" ): Unexpected ending"<<endl;}cout<<"This is invalid!"<<endl<<endl;ofs<<"This is invalid!"<<endl<<endl;return 0;}else{if((table[getRow(proStack.top())][getCol(a)]).left!=' '){char pst=proStack.top();int n=table[getRow(pst)][getCol(a)].ln;int k=0;ofs<<table[getRow(pst)][getCol(a)].left<<"->"<<table[getRow(pst)][getCol(a)].right[0]<<table[getRow(pst)][g etCol(a)].right[1]<<table[getRow(pst)][getCol(a)].right[2]<<endl;proStack.pop();while (n>0){//cout<<n<<" "<<table[getRow(pst)][getCol(a)].right[n-1]<<endl;proStack.push(table[getRow(pst)][getCol(a)].right[n-1]);n--;}}else{if(a!='#'){cout<<"ERROR(LINE "<<getLine()<<" ): "<<a<<" cannot be matched"<<endl;ofs<<"ERROR(LINE "<<getLine()<<" ): "<<a<<" cannot be matched"<<endl;}else{cout<<"ERROR(LINE "<<getLine()<<" ): Unexpected ending"<<endl;ofs<<"ERROR(LINE "<<getLine()<<" ): Unexpected ending"<<endl;}cout<<"This is invalid!"<<endl<<endl;ofs<<"This is invalid!"<<endl<<endl;return 0;}}}}}实验结果:●输入(in.txt)●输出1输出2(out.txt)实验总结:原本以为处理四则运算赋值将会很困难,但在使用LL(1)后发现,思路还是挺清晰简单的,但在实验过程中,由于LL(1)不能出现左递归和左公因子,不得不将其消除,原本简单的产生式一下变多了,而在产生式理解上也没有原来直观,不过其状态复杂度没有LR高,故仍选择该方法。
语法分析器计

1.2 语法分析器设计语法分析是编译程序的核心部分,其主要任务是确定语法结构,检查 语法错误,报告错误的性质和位置,并进行适当的纠错工作.法分析的方法有多 种多样,常用的方法有递归子程序方法、运算符优先数法、状态矩阵法、LL(K) 方法和 LR(K)方法。
归纳起来,大体上可分为两大类,即自顶向下分析方法和 自底向上分析方法. Syntax 进行语法分析.对于语法分析,这里采用 LR(1)分析 法,判断程序是否满足规定的结构.构造 LR(1)分析程序,利用它进行语法分析, 判断给出的符号串是否为该文法识别的句子,了解 LR(K)分析方法是严格的从 左向右扫描,和自底向上的语法分析方法。
1.2.1 LR分析过程的设计思想及算法1:LR-table.txt:存放分析表,其中正数表示移进,负数表示归约,100 表示接受状态, 0 表示不操作。
2:grammar.txt 存放文法开始符号 3:lengh.txt 存放产生式右部字符长度 4:inpur.txt 输入的程序语法规则定义的文法,如下: (0) Z---S (1) S---AB (2) A---->CDE (3) C---void (4) D---main (5) E---() (6) B---{F} (7) F---GF (8) F---G (9) G--->HIJ (10) H--int (11) I--KLM (12) K--character(13) L--= (14) M--->num (15) J--;根据上面文法画出的分层有限自动机并根据分层自动机构造的 LR(1)分析表:vo m ( { i ch = nu S A B C D E F G H I J K L M } ; #id ai )n armnt021831Ac2-33454-45676-57-281909-11205111 13511121-261235111-43581-47115612701-1621117981--811551--9992220122-1132232242 32-4142-5111.2.2 程序核心代码和注释:public void analyzer() { //*************************** //循环读取grammar.txt //*************************** /*此处代码略*/ //*************************** //循环读取 lengh.txt //*************************** /*此处代码略*/ //**************************** // 读入文件,进行语法分析 //**************************** string strReadFile; strReadFile="input.txt"; myTextRead.myStreamReader=new StreamReader(strReadFile); string strBufferText; int wid =0; Console.WriteLine("分析读入程序(记号ID):\n"); do { strBufferText =myTextRead.myStreamReader.ReadLine();if(strBufferText==null) break;foreach (String subString in strBufferText.Split()) {if(subString!="") {int ll; if(subString!=null) {ll= subString.Length; //每一个长度 } else {break; } int a=ll+1; char[] b = new char[a];StringReader sr = new StringReader(subString);sr.Read(b, 0, ll);//把substring 读到char[]数组里int sort=(int)b[0];// word[i] 和 wordNum[i]对应//先识别出一整个串,再根据开头识别是数字还是字母Word[wid]=subString;if(subString.Equals("void")){wordNum[wid]=0;}else{if(subString.Equals("main")){wordNum[wid]=1;}else{if(subString.Equals("()")){wordNum[wid]=2;}else{if(subString.Equals("{")){wordNum[wid]=3;}else{if(subString.Equals("int")){wordNum[wid]=4;}else{if(subString.Equals("=")){wordNum[wid]=6;}else{if(subString.Equals("}")){wordNum[wid]=22;}else{if(subString.Equals(";")){wordNum[wid]=23;}else //识别变量和数字{if(sort>47&sort<58){wordNum[wid]=7;}else{wordNum[wid]=5;}}}} } } } } } Console.Write(subString+"("+wordNum[wid]+")"+" ");wid++; } } Console.WriteLine("\n"); }while (strBufferText!=null); wordNum[wid]=24; myTextRead.myStreamReader.Close();//********************************* //读入LR分析表 // //***********************************/*此处代码略*/int[] state = new int[100]; string[] symbol =new string[100]; state[0]=0; symbol[0]="#"; int p1=0; int p2=0; Console.WriteLine("\n按文法规则归约顺序如下:\n"); //*************** // 归约算法如下所显示 //*************** while(true) {int j,k;j=state[p2];k=wordNum[p1]; t=LR[j,k]; //当出现t为0的时候 if(t==0) {//错误类型 string error;"+error);if(k==0) error="void";else if(k==1) error="main";else if(k==2) error="()";else if(k==3) error="{";else if(k==4) error="int";else if(k==6) error="=";else if(k==22) error="}";else if(k==23) error=";";else error="其他错误符号";Console.WriteLine("\n检测结果:"); Console.WriteLine("代码中存在语法错误"); Console.WriteLine("错误状况:错误状态编号为 "+j+" 读头下符号为break;}else{if(t==-100)//-100为达到接受状态{Console.WriteLine("\n");Console.WriteLine("\n检测结果:");Console.WriteLine("代码通过语法检测");break;}if(t<0&&t!=-100)//归约{string m=grammar[-t];Console.Write(m+" ");//输出开始符int length=lengh[-t]; p2=p2-(length-1); Search mySearch=new Search(); int right=mySearch.search(m); if(right==0) {Console.WriteLine("\n"); Console.WriteLine("代码中有语法错误"); break; } int a=state[p2-1]; int LRresult= LR[a,right]; state[p2]=LRresult; symbol[p2]=m; } if(t>0) { p2=p2+1; state[p2]=t; symbol[p2]=Convert.ToString(wordNum[p1]); p1=p1+1; } }} myTextRead.myStreamReader.Close(); Console.Read(); }示例:1:void main () {int i = 8 ; int aa = 10 ; int j = 9 ; }2:void main (){ intq i = 8 ; int aa = 10 ; int j = 9 ; } 对于 intq i=8 中 intq 这个错误类型,词法分析通过,而语法分析正确识别出了错误,达到 预期目标 产生出错信息: 运行显示如下:1.3 中间代码生成器设计进入编译程序的第三阶段:中间代码产生阶段。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
LR分析程序设计
1实验目的
(1)构造LR 分析程序,利用它进行语法分析,判断给出的符号串是否为
该文法识别的句子;
(2)了解LR分析方法是严格的从左向右扫描,和自底向上的语法分析方法。
2实验内容和实验要求
(1)LR分析器能够构造来识别所有能用上下文无关文法写的程序设计语言的结构。
(2)LR分析方法是已知的最一般的无回溯,移进-归约方法,它能够和其他移进-归约方法一样有效地实现。
(3)LR方法能分析的文法类是预测分析法能分析的文法类的真超集。
3 待分析的语法描述
E->vI:T
I->I,i|i
T->r
4算法描述
LR分析法基本思想
LR分析法是一种能够根据分析栈中的文法符号串(状态)和向右顺序查看第k个输入字符就能够唯一确定LR(k)分析器的动作是移进还是用哪一条产生式归约的分析方法。
采用LR(0)分析法进行本次实验,即无需向前查看输入符号就能够确定分析器的动作。
实现方法
LR(0)分析器由三个部分组成:
(1)总控程序,也可以称为驱动程序。
对所有的LR分析器总控程序都是相同的。
(2)分析表,不同的文法分析表将不同,同一个文法采用的LR分析器不同时,分析表将不同,分析表又可以分为动作表(ACTION)和状态转换(GOTO)表两个部分,它们都可用二维数组表示。
由于它是总控程序的依据,所以在程序的第一部分就已经定义好。
(3)分析栈,包括文法符号栈和相应的状态栈,它们均是先进后出栈。
分析器的动作就是由栈顶状态和当前输入符号所决定。
(4)LR分析器及时察觉语法错误,快到自左向右扫描输入的最大可能。
为了使一个文法是LR的,只要保证当句柄出现在栈顶时,自左向右扫描的移进-归约分析器能够及时识别它便足够了。
当句柄出现在栈顶时,LR分析器必须要扫描整个栈就可以知道这一点,栈顶的状态符号包含了所需要的一切信息。
如果仅知道栈内的文法符号就能确定栈顶是什么句柄。
由于LR分析表的转移函数本
质上就是这样的有限自动机,因为,如果这个识别句柄的有限自动机自底向上读栈中的文法符号的话,它达到的状态正是这时栈顶的状态符号所表示的状态,所以,LR分析器可以从栈顶的状态确定它需要从栈中了解的一切。
算法分析
SP为栈指针,S[i]为状态栈,X[i]为文法符号栈。
状态转换表用GOTO[i,X]=j表示,规定当栈顶状态为i,遇到当前文法符号为X时应转向状态j,X为终结符或非终结符。
ACTION[i,a]规定了栈顶状态为i时遇到输入符号a应执行。
动作有四种可能:(1)移进:
action[i,a]= Sj:状态j移入到状态栈,把a移入到文法符号栈,其中i,j表示状态号。
(2)归约:
action[i,a]=r k:当在栈顶形成句柄时,则归约为相应的非终结符A,即文法中有A-B的产生式,若B的长度为R(即|B|=R),则从状态栈和文法符号栈中自顶向下去掉R个符号,即栈指针SP减去R,并把A移入文法符号栈内,j=GOTO[i,A]移进状态栈,其中i为修改指针后的栈顶状态。
(3)接受acc:
当归约到文法符号栈中只剩文法的开始符号S时,并且输入符号串已结束即当前输入符是'#',则为分析成功。
(4)报错:
当遇到状态栈顶为某一状态下出现不该遇到的文法符号时,则报错,说明输入端不是该文法能接受的符号串。
5 总控程序框图
6程序清单和程序运行结果
程序清单见磁盘
程序运行结果如下:
显示所要分析的文法,并且输入所要分析的句型:
2.输入所要分析的句型
(1)若所要分析的字符串不为该文法的句型,出错分析:例若为i-ii#,则出错显示如下:
(2)若正确输入该文法的句型如:vi,i:r#,则分析结果如下:
(3)选择是否要继续分析,若需要则选Y,若不需要,则选择N,则退出程序。
7实验感想
这是在做课程设计之前的最后一个实验:自底向下的语法分析法。
这种方法来分析的固定的文法,是可以凸现其通用性,虽然它没有算符优先分析方法效率那么高。
但是由于算符优先分析法是将当前句型中的最左素短语而不是句柄(最左直接短语)作为规约的子串,省略了所有单非终极符产生式对应的归约步骤,因此在分析过程中,虽然采取了相应的一些检查措施,但仍然有可能将错误的输入符号串归约为正确的句子。
而且很多的程序设计语言的文法也很难满足算符优先文法的条件,所以它只能局限在表达式的语法分析中。
但是对于LR分析法,其分析的过程更加符合程序设计语言文法的要求,这样使之通用性加强。
同样在做这个实验的时候,发现由于在构造goto表以及action表中的相关参数中,没有非常的有条理这样就导致了在程序分析过程中没有得到非常整齐的界面。
经过定义循环变量控制空格符的多少,使表格输出合理化。
由于LR分析方
法是可以有很多中形式的,虽然这次我只是选择了其中的一种方法。
但是实际上比较通行的时SLR(1)分析方法,因为它可以解决相应的一些移进归约,规约规约冲突。
通过这个实验,在进一步了解到编译程序前端三个步骤的重要以及运用的范围,同时也加深了对高级语言运用的熟练程度。