博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
2021-06-04
阅读量:3958 次
发布时间:2019-05-24

本文共 11835 字,大约阅读时间需要 39 分钟。

A. 自顶向下语法分析方法

自顶向下分析法也称面向目标的分析方法,也就是从文法的开始符号出发企图推导出与输入的单词串完全相匹配的句子,若输入串是给定文法的句子,则必能推出,反之必然出错。自顶向下的确定分析方法需对文法有一定的限制,但由于实现方法简答、直观,便于手工构造或自动生成语法分析器,因而仍是目前常用的方法之一。而自顶向下的不确定分析方法是带回溯的分析方法,这种方法实际上是一种穷举的试探方法,因此效率低,代价高,因而极少使用。

LL(1)分析

自顶向下的确定分析方法,是从文法的开始符号出发,考虑如何根据当前的输入符号(单词符号)唯一地确定选用哪个产生式替换相应非终结符以往下推导,或如何构造一棵相应的语法树。当我们需要选用自顶向下的确定分析技术时,首先必须判别所给文法是否是LL(1)文法。因而对所给文法需计算FIRST、FOLLOW、SELECT集合,进而判别文法是否为LL(1)文法。LL(1)文法表示了自顶向下方法能够处理的一类文法。LL(1)第一个“L”表示从左向右扫描输入,第二个“L”表示产生最左推导,而“1”则表示每一步中只需要向前看一个输入符号来决定语法分析动作。类似也可以有LL(k)文法,也就是需向前查看k个符号才可以确定选用哪个产生式。通常采用k=1,个别情况采用k=2。LL(1)文法已经足以描述大多数程序设计语言构造,虽然在为源语言设计适当的文法时需要多加小心。比如,左递归的文法和二义性的文法都不可能是LL(1)的。一个文法G是LL(1)的,当且仅当G的任意两个不同的产生式A-》α|β满足下面的条件:

(1) 不存在终结符号a使得α和β都能够推导出以a开头的串。

(2) α和β中最多只有一个可以推导出空串。

(3) 如果βÞε,那么α不能推导出任何以FOLLOW(A)中某个终结符号开头的串。类似地,如果αÞε,那么β不能推导出任何以FOLLOW(A)中某个终结符号开头的串。

我们知道当文法不满足LL(1)时,则不能用确定的自顶向下分析,但在这种情况下可用不确定的自顶向下分析,也就是带回溯的自顶向下分析。引起回溯的原因是在文法中当关于某个非终结符的产生式有多个候选时,而面临当前的输入符无法确定选用唯一的产生式,从而引起回溯。引起回溯的情况大致有3种:

(1)由于相同的左部的产生式的右部FIRST集交集不为空而引起回溯;

(2) 由于相同左部非终结符的右部存在能Þ*ε的产生式,且该非终结符的FOLLOW集中含有其他产生式右部FIRST集的元素;

(3) 由于文法含有左递归而引起回溯。

B. 自底向上语法分析方法

自底向上分析,也称移近-归约分析,粗略地说它的实现思想是对输入符号串自左向右进行扫描,并将输入符逐个移入一个后进先出栈中,边移入边分析,一旦栈顶符号串形成某个句型的句柄或可归约串时(该句柄或可归约串对应某产生式的右部),就用该产生式的左部非终结符代替相应右部的文法符号串,这称为一步归约。重复这一过程直到归约到栈中只剩文法的开始符号时则为分析成功,也就确认输入串是文法的句子。

目前最流行的自底向上语法分析器都基于所谓的LR(k)语法分析的概念。其中,“L”表示对输入进行从左到右的扫描,“R”表示反向构造出一个最右推导序列,而k表示在做出语法分析决定时向前看k个输入符号。

LR语法分析技术优点:

  1. 对于几乎所有的程序设计语言构造,只要能够写出该构造的上下文无关文法,就能够构造出识别该构造的LR语法分析器。确实存在非LR的上下文无关文法,但一般来说,常见的程序设计语言构造都可以避免使用这样的文法。

2. LR语法分析方法是已知的最通用的无回溯移入-归约分析技术,并且它的实现可以和其他更原始的移入-归约方法一样高效。

3. 一个LR语法分析器可以在对输入进行从左到右扫描时尽可能早地检测到错误。

4. 可以使用LR方法进行语法分析的文法类是可以使用预测方法或LL方法进行语法分析的文法类的真超集。一个文法是LR(k)的条件是当我们在一个最右句型中看到某个产生式的右部时,我们再向前看k个符号就可以决定是否使用这个产生式进行归约。这个要求比LL(k)文法的要求宽松很多。对于LL(k)文法,我们在决定是否使用某个产生式时,只能向前看该产生式右部推导出的串的前k个符号。因此,LR文法能够比LL文法描述更多的语言就一点也不奇怪了。

LR方法的主要缺点是为了一个典型的程序设计语言文法手工构造LR分析器的工作量非常大,k愈大构造愈复杂,实现比较困难。常见的LR方法有LR(0)、SLR(1)、LALR(1)和LR(1),其中SLR(1)和LALR(1)分别是LR(0)和LR(1)的一种改进。下面主要介绍这四种LR分析方法:

1. LR(0)分析

LR(0)分析器是在分析过程中不需向右查看输入符号,因而它对文法的限制较大,对绝大多数高级语言的语法分析器是不能适用的,然而,它是构造其他LR类分析器的基础。LR(0)分析表构造的思想和方法是构造其他LR分析表的基础,它是LR(0)分析器的重要组成部分,它是总控程序分析动作的依据。对于不同的文法,LR(0)分析表不同,它可以用一个二维数组表示,行标为状态号,列标为文法符号和“#”号,分析表的内容可由两部分组成,一部分为动作(ACTION)表,它表示当前状态下所面临输入符应做的动作是移近、归约、接受或出错,动作表的行标只包含终结符和“#”,另一部分为转换(GOTO)表,它表示在当前状态下面临文法符号时应转向的下一个状态,相当于识别活前缀的有限自动机DFA 的状态转换矩阵。我们知道LR(0)对文法的限制较大,它要求对一个文法的LR(0)项目集规范族不存在移近-归约,或归约-归约冲突。所谓移近-归约冲突也就是在一个项目集中移近和归约项目同时存在,而归约-归约冲突是在一个项目集中归约和归约项目同时存在。

2. SLR(1)分析

由于大多数实用的程序设计语言的文法不能满足LR(0)文法的条件,经过改进后得到了一种新的SLR(1)文法,其思想是基于容许LR(0)规范族中有冲突的项目集(状态)用向前查看一个符号的办法来进行处理,已解决冲突。因为只有对有冲突的状态才向前查看一个符号,以确定做哪种动作,所以称这种分析方法为简单的LR(1)分析法,用SLR(1)表示。通常对于LR(0)规范族的一个项目集I可能含有多个项目和多个归约项目,假设项目集I中有m个移近项目:A1-》α1 •α1β1,A2-》α2 •α2β2,… , Am-》αm •αmβm;同时含有n个归约项目为:B1-》γ1• ,B1-》γ1• , … ,Bn-》γn• 只要集合{α1 ,α2 ,… ,αm}和FOLLOW(B1),FOLLOW(B2),… ,FOLLOW(Bn)两两交集都为空,那么我们可以考察当前输入符号决定动作,解决冲突。尽管采用SLR(1)方法能够对某些LR(0)项目集规范族中存在动作冲突的项目集,通过用向前查看一个符号的办法来解决冲突,但是仍有很多文法构造的LR(0)项目集规范族存在的动作冲突不能用SLR(1)方法解决。当集合{α1 ,α2 ,… ,αm}和FOLLOW(B1),FOLLOW(B2),… ,FOLLOW(Bn)之间存在交集不为空的情况,则不能根据当前输入符号决定动作,存在动作冲突。

3. LR(1)分析

可以看出SLR(1)方法虽然相对LR(0)有所改进,但仍然存在着多余归约,也说明SLR(1)方法向前查看一个符号的方法仍不够确切,LR(1)方法恰好是要解决SLR(1)方法在某些情况下存在的无效归约问题。LR(1)对比SLR(1)而言,多了向前搜索符这个概念。根据项目集构造原则有:若[A-》α•Bβ] ∈项目集I时。则[B-》•γ]也∈I(B-》γ为一产生式)。由此不妨考虑,把FIRST(β)作为用产生式B-》γ归约的搜索符,称为向前搜索符,作为归约时查看的符号集合用以代替SLR(1)分析中的FOLLOW集,把此搜索符号的集合也放在相应项目的后面,这种处理方法即为LR(1)方法。在多数情况下,对LR(1)的归约项目不存在任何无效归约,但同一个文法的LR(1)项目集的个数比LR(0)项目集的个数多,甚至可能多好几倍。这是由于同一个LR(0)项目集的搜索符集合不同,多个搜索符集合则对应着多个LR(1)项目集。这可以看成是LR(1)项目集的构造使某些同心集进行了分裂,因而项目集的个数增多了。如果一个文法的LR(1)分析表不含多重入口时,(即任何一个LR(1)项目集中无移近-归约冲突或归约-归约冲突),则称该文法为LR(1)文法。LR(1)文法已能满足当前绝大多数高级语言编译程序的需要。

4. LALR(1)分析

LR(1)分析表的构造对搜索符的计算方法比较确切,对文法放宽了要求,也就是适应的文法类广,可以解决SLR(1)方法解决不了的问题,但是,由于它的构造对某些同心集的分裂可能对状态数目引起剧烈的增长,从而导致存储容量的急剧增长,也使应用受到了一定的限制,为了克服LR(1)的这种缺点,我们可以采用对LR(1)项目集规范族合并同心集的方法,若合并同心集后不产生新的冲突,则为LALR(1)项目集。它的状态个数与LR(0)、SLR(1)的相同。关于合并同心集有几个问题需要说明:1)同心集是指心相同的项目集合并在一起,因此同心集合并后心仍然相同,只是超前搜索符集合为各同心集超前搜索符集合的合集;2)对于合并同心集后的项目集经转换函数所达仍为同心集;3)若文法是LR(1)文法,合并同心集后若有冲突也只可能是归约-归约冲突,而不可能产生移近-归约冲突;4)合并同心集后对某些错误发现的时间会产生推迟现象,但错误的出现位置仍是准确的。这意味着LALR(1)分析表比LR(1)分析表对同一输入串的分析可能会有多余归约

  
语法分析器代码:

/*    begin a:= 9; x:=2*3;b:=a+x end#    x:= a+b*c end #*/#include 
#include
#include
#define _KEY_WORD_END "waiting for your expanding"typedef struct{ int typenum; char *word;}WORD;char input[255];char token[255]="";int p_input;int p_token;char ch;char* rwtab[] = {"begin","if","then","while","do","end",_KEY_WORD_END};WORD * scaner();WORD* oneword = new WORD;int syn,kk;void expression();void factor(){ if(syn == 10 || syn == 11) { oneword = scaner(); syn = oneword->typenum; printf("%s %d\n",oneword->word,syn); } else if(syn == 27) { oneword = scaner(); syn = oneword->typenum; printf("%s %d\n",oneword->word,syn); expression(); if(syn == 28) { oneword = scaner(); syn = oneword->typenum; printf("%s %d\n",oneword->word,syn); } else{ printf("右括号错误\n"); kk = 1; } } else { printf("表达式错误\n"); kk = 1; } return;}void term(){ factor(); while(syn == 15 || syn == 16) { oneword = scaner(); syn = oneword->typenum; printf("%s %d\n",oneword->word,syn); factor(); } return;}void expression(){ term(); while(syn == 13 || syn == 14) { oneword = scaner(); syn = oneword->typenum; printf("%s %d\n",oneword->word,syn); term(); } return;}void statement(){ if(syn == 10) { oneword = scaner(); syn = oneword->typenum; printf("%s %d\n",oneword->word,syn); if(syn == 18) { oneword = scaner(); syn = oneword->typenum; printf("%s %d\n",oneword->word,syn); expression(); } else{ printf("赋值号错误\n"); kk = 1; } } else{ printf("语句错误\n"); kk = 1; } return;}void yucu(){ statement(); while(syn == 26) { oneword = scaner(); syn = oneword->typenum; printf("%s %d\n",oneword->word,syn); statement(); } return;}void Irparser(){ if(syn == 1) { oneword = scaner(); syn = oneword->typenum; printf("%s %d\n",oneword->word,syn); yucu(); if(syn==6) { oneword = scaner(); syn = oneword->typenum; if(syn == 0 && (kk == 0)) printf("success\n"); } else{ if(kk != 1) { printf("缺少end错误\n"); kk = 1; } } } else{ printf("begin错误\n"); kk = 1; } return;}int main(){ int over = 1; printf("Enter Your words(end with #):"); scanf("%[^#]s",input); p_input = 0; printf("Your words:\n%s\n",input); //获取下一个单词符号 oneword = scaner(); syn = oneword->typenum; Irparser(); printf("\npress # to exit:"); scanf("%[^#]s",input); return 0;}char m_getch(){ ch = input[p_input]; p_input = p_input+1; return(ch);}void getbc(){ while(ch == ' '||ch == 10) { ch = input[p_input]; p_input++; }}void concat(){ token[p_token] = ch; p_token++; token[p_token] = '\0';}int letter(){ if((ch >= 'a'&& ch <= 'z')|| (ch >='A'&&ch <= 'Z')) return 1; else return 0;}int digit(){ if(ch >= '0'&&ch <='9')return 1; else return 0;}int reserve(){ int i = 0; while(strcmp(rwtab[i],_KEY_WORD_END)) { if(!strcmp(rwtab[i],token)) return i+1; i++; } return 10;}void retract(){ p_input--;}char* dtb(){ return NULL;}WORD* scaner(){ WORD* myword = new WORD; myword->typenum = 10; myword->word = ""; p_token = 0; m_getch(); getbc(); if(letter()) { while(letter()||digit()) { concat(); m_getch(); } retract(); myword->typenum = reserve(); myword->word = token; return(myword); } else if(digit()) { while(digit()) { concat(); m_getch(); } retract(); myword->typenum = 11; myword->word = token; return(myword); } else switch(ch) { case'=':m_getch(); if(ch == '=') { myword->typenum = 25; myword->word = "=="; return(myword); } retract(); myword->typenum = 21; myword->word = "="; return(myword); break; case'+': myword->typenum = 13; myword->word = "+"; return(myword); break; case'-': myword->typenum = 14; myword->word = "-"; return(myword); break; case'*': myword->typenum = 15; myword->word = "*"; return(myword); break; case'/': myword->typenum = 16; myword->word = "/"; return(myword); break; case'(': myword->typenum = 27; myword->word = "("; return(myword); break; case')': myword->typenum = 28; myword->word = ")"; return(myword); break; case'[': myword->typenum = 28; myword->word = "["; return(myword); break; case']': myword->typenum = 29; myword->word = "]"; return(myword); break; case'{': myword->typenum = 30; myword->word = "{"; return(myword); break; case'}': myword->typenum = 31; myword->word = "}"; return(myword); break; case',': myword->typenum = 32; myword->word = ","; return(myword); break; case':':m_getch(); if(ch == '=') { myword->typenum = 18; myword->word = ":="; return(myword); } retract(); myword->typenum = 17; myword->word = ":"; return(myword); break; case';': myword->typenum = 26; myword->word = ";"; return(myword); break; case'>':m_getch(); if(ch == '=') { myword->typenum = 24; myword->word = ">="; return(myword); } retract(); myword->typenum = 23; myword->word = ">"; return(myword); break; case'<':m_getch(); if(ch == '=') { myword->typenum = 22; myword->word = "<="; return(myword); } else if(ch == '>') { myword->typenum = 21; myword->word = "<>"; return(myword); } retract(); myword->typenum = 20; myword->word = "<"; return(myword); break; case'!':m_getch(); if(ch == '=') { myword->typenum = 40; myword->word = "!="; return(myword); } retract(); myword->typenum = -1; myword->word = "ERROR"; return(myword); break; case'#': myword->typenum = 0; myword->word = "#"; return(myword); break; case'\0': myword->typenum = 0; myword->word = "OVER"; return(myword); break; default: myword->typenum = -1; myword->word = "ERROR"; return(myword); }}

转载地址:http://ehxzi.baihongyu.com/

你可能感兴趣的文章
图片的三级缓存机制
查看>>
自定义标签库(Tag library)
查看>>
自定义标签库(Tag library)
查看>>
深入Java集合学习系列(一)
查看>>
深入Java集合学习系列(一)
查看>>
深入Java集合学习系列(二):
查看>>
图解Spring AOP
查看>>
性能调优之Weblogic调优
查看>>
性能调优之性能参数指标
查看>>
POJ3009---冰壶游戏(深搜剪枝+回溯)
查看>>
POJ3669---跳炸弹(广搜)
查看>>
POJ---1384Piggy-Bank (完全背包+装满问题)
查看>>
并查集基础知识
查看>>
POJ1182---食物链(带权并查集~技巧性超强的解法)
查看>>
POJ2492---A Bug's Life(做完食物链,再秒这个)
查看>>
POJ2063---Investment(完全背包)
查看>>
POJ1458---(最长公共子序列最基础题)
查看>>
POJ3356---(最长公共子序列)
查看>>
二叉树基础知识大全(核心理解遍历)
查看>>
03-树1 树的同构(25 分) 2017秋 数据结构 陈越、何钦铭
查看>>