句法分析分为 句法结构分析 和 依存关系分析 两种。以获取整个句子的句法结构为目的的称为 完全句法分析 ,而以获得局部成分为目的的语法分析称为 局部分析 ,依存关系分析简称 依存分析 。
一般而言,句法分析的任务有三个:
判断输出的字符串是否属于某种语言
消除输入句子中词搏森法和结构等方面的歧义
分析输入句子的内部结构,如成分构成、上下文关系等。
第二三个任务一般是句法分析的主要任务。
一般来说,构造一个句法分析器需要考虑两部分工作:一部分是语法的形式化表示和词条信息描述问题,形式化的语法规则构成了规则库,词条信息等由词典或同义词表等提供,规则库与词典或同义词表构成了句法分析的知识库;另一部分就是基于知识库的解析算法了。
语法形式化属于句法理论研究的范畴,目前在自然语言处理中广泛使用的是上下文无关文法(CFG)和基于约束的文法,后者又称合一文法。
简单的讲,句法结构分析方法可以分为基于规则的分析方法和基于统计的分析方法两大类。
基于规则的句法结构分析方法的基本思路是,由人工组织语法规则,建立语法知识库,通过条件约束和检查来实现句法结构歧义的消除。
根据句法分析树形成方向的区别,人们通常将这些方法划分为三种类型:自顶向下的分析方法,自底向上的分析方法和两者相结合的分析方法。自顶向下分析算法实现的是规则推导的过程,分析树从根结点开始不断生长,最后形成分析句子的叶结点。而自底向上分析算法的实现过程恰好想法,它是从句子符号串开始,执行不断规约的过程,最后形成根节点。
基于规则的语法结构分析可以利用手工编写的规则分析出输入句子所有可能的句法结构;对于特定领域和目的,利用有针对性的规则能够较好的处理句子中的部分歧义和一些超语法(extra-grammatical)现象。
但对于一个中等长度的输入句子来说,要利用大覆盖度的语法规则分析出所有可能的句子结构是非常困难的,而且就算分析出来了,也难以实现有效的消歧,并选择出最有可能的分析结果;手工编写的规则带有一定的主观性,还需要考虑到泛化,在面对复杂语境时正确率难以保证;手工编写规则本身就是一件大工作量的复杂劳动,而且编写的规则领域有密切的相关性,不利于句法分析系统向其他领域移植。
基于规则的句法分析算法能够成功的处理程序设计语言的编译,而对于自然语言的处理却始终难以摆脱困境,是因为程序设计语言中使用的知识严格限制的上下文无关文法的子类,但自然语言处理系统中所使用的形式化描述方法远远超过了上下文无关文法的表达能力;而且人们在使用程序设计语言的时候,一切表达方式都必须服从机器的要求,是一个人服从机器的过程,这个过程是从语言的无限集到有限集的映射过程,而在自然语言处理中则恰恰相反,自然语言处理实现的是机器追踪和服从人的语言,从语言的有限集到无限集推演的过程。
完全语法分析
基于PCFG的基本分析方法
基于概率上下文无关文法的短语结构分析方法,可以说是目前最成功的语法驱动的统计句法分析方法,可以认为是规则方法与统计方法的结合。
PCFG是CFG的扩展,举个例子:
PCFG
当然,同一个符号不同生成式的概率之和为1。NP是名词短语、VP是动词短语、PP是介词短语。
基于PCFG的句法分析模型,满足以下三个条件:
位置不变性:子树的概率不依赖于该子树所管辖的单词在句子中的位置
上下文无关性:子树的概率不依赖于子树控制范围以外的单词
祖先无关性:子树的概率不依赖于推导出子树的祖先节点
根据上述文法,『He met Jenny with flowers』有两种可能的语法结构:
而且我们可以通过将树中的所有概率相乘,得到两棵子基培亩树的整体概率,从中选择概率更大的子树作为最佳结构。
与HMM类似,PCFG也有三个基本问题:
给定一个句子W=w1w2…wn和文法G,如何快速计算概率P(W|G)
给定一个句子W=w1w2…wn和文法G,如何选择该句子的最佳结构?即选择句法结构树t使其具有最大概率
给定PCFG G和句子W=w1w2…wn,如何调节G的概率参数,使句子的概率最大
首先是第一个问题,HMM中我们用的是前向算法和后向算法来计算观察序列O概率,相似的,这里我们用的是内向算法和外向算法来计算P(W|G) 。
首先我们定义内向变量αij(A),与前向变量相似但又有不同,αij(A)即非终结符A推导出W中字串wiw(i+1)…wj的概率。那P(W|G)自然就等于α1n(S)了,S是起始符号,计算的就是由起始符号S推导出整个句子W=w1w2…wn的概率。
所以只要有αij(A)的递归公式就能计算出P(W|G),递归公式如下:
根据定义,αii(A)自然就等同于符号A输出wi的概率;而αij(A)的计算思路是,这个子串wiw(i+1)…wj可以被切成两部分处理,前一部分wiw(i+1)…wk由非终结符号B生成,后一部分wkw(k+1)…wj由非终结符号C生成,而BC由A生成。这样将概率依次相乘,即可将一个大问题划分为两个小问题处理,两个小问题又可以进一步划分直到不能划分为止,然后递归回来得到结果。
这里给一张内向变量计算方法示意图:
这个问题也可以用外向算法来解决。
首先定义外向变量,βij(A)是,初始符号S在推导出语句W=w1w2…wn的过程中,产生符号串w1w2…w(i-1)Aw(j+1)…wn的概率(隐含着A会生成wiw(i+1)…wj)。也就是说βij(A)是S推导出除了以A节点为根节点的子树以外的其他部分的概率。
《统计自然语言处理(第二版)》这本书里讲错了,这里我给出我自己的理解,书里给的算法步骤如下:
很明显的错误,初始化都把结果初始化了,那这个算法还算什么,直接等于1就完了呗。
这是作者对外向变量定义理解模糊的问题,上面给了外向变量的定义,里面有一句话『隐含着A会生成wiw(i+1)…wj』,那问题在于,A会生成wiw(i+1)…wj,这到底算是条件还是推论。
看这个算法的初始化的意思,说β1n(A),在A=S的时候,为1,不等于S为0,意思是什么?意思就是『隐含着A会生成wiw(i+1)…wj』这句话是条件,β1n(S)已经隐含了S生成W=w1w2…wn了,所谓的w1w2…w(i-1)Aw(j+1)…wn也就不存在了,只剩下一个S->S了,所以概率自然为1。
但是在第三步这个地方,作者理解成什么意思了呢?作者又把『隐含着A会生成wiw(i+1)…wj』这句话当成推论了,认为在β1n(S),里S会生成W=w1w2…wn是推论,那真是就正好了,要求的结果就是S生成W=w1w2…wn,这不就结束了吗,结果就导致了这个算法第一步初始化都把结果初始化了。
那我的理解是什么呢,通过这个公式计算出来的β1n(S),确实是正确的,意义实际上也是包含了『隐含着A会生成wiw(i+1)…wj』这句话是推论,但是右侧式子里由于不断递归而产生的β1n(S),是把『隐含着A会生成wiw(i+1)…wj』这句话当条件的,所以计算上没有问题。
我倾向于为第三步中的β1n(S)加一个星号,以表明意义的不同。
书中还给了个外向变量的计算方法示意图,我觉得也是莫名其妙:
他说βij(A)是这两种情况的概率和,这我们知道j比i大,那这图里这个k既比i小又比j大,这不是搞笑吗。只能说图上这俩C就不是一个C,k也不是一个k。
那我为什么会理解成一个呢,除了字母相同,他前面还这么讲『必定运用了形如B->AC或者B->CA的规则』、『运用B->AC或者B->CA两种规则的情况』,这明显就是给人以顺序交换的误解。
另外,还在内向变量的使用上前后不一,可以说这本书里对外向算法的讲解是非常失败的。而且对外向算法的计算仍然需要用到内向算法的递归,那真的直接用内向算法就好了,外向算法还要多定义变量。
然后是第二个问题,选择句子的最佳结构,也即给定一个句子W=w1w2…wn和文法G,
选定拥有最大概率的语法结构树。这一问题与HMM中类似,仍然采用动态规划的思想去解决。最后利用CYK算法去生成拥有最大概率的语法结构树。
第三个问题是给定PCFG G和句子W=w1w2…wn,如何调节G的概率参数,使句子的概率最大,与HMM相对的,PCFG这里采用的算法名叫内外向算法。与前后向算法相同,也属于一种EM算法,其基本思想是,首先给G的产生式随机地赋予一个概率值(满足归一化条件),得到文法G0,然后根据G0和训练数据,可以计算出每条规则使用次数的期望值,用期望值进行最大似然估计,得到语法G的新参数值,新的语法记作G1,然后循环执行该过程,G的参数概率将收敛于最大似然估计值。
PCFG只是一种特殊的上下文无关文法模型,根据PCFG的模型和句子,具体去对句子做语法分析,生成语法结构树,靠的是还是CYK算法。CYK算法是一个用来判定任意给定的字符串W是否属于一个上下文无关文法的算法。
基于PCFG的句法分析模型存在有许多问题,比如因为PCFG没有对词汇进行建模,所以存在对词汇信息不敏感的问题。因此人们提出了词汇化的短语结构分析器,有效的提升了基于PCFG的句法分析器的能力。
而且,我们上面也提到了PCFG的三个独立性假设,这也导致了规则之间缺乏结构依赖关系(就像HMM的三个假设也不完全合理一样),而在自然语言中,生成每个非终结符的概率往往是与其上下文结构有关系的,所以有人提出了一种细化非终结符的方法,为每个非终结符标注上其父节点的句法标记信息。
D. Klein提出了带有隐含标记的上下文无关文法(PCFG with latent annotations,PCFG-LA),使得非终结符的细化过程可以自动进行,并且在使用EM算法优化时,为避免到达局部最优,对其进行了改进,提出了一种层次化的『分裂-合并』策略,以期获取一个准确并且紧凑的PCFG-LA模型。基于PCFG-LA的Berkeley Parser作为非词汇化句法分析器的代表,无论是性能表现还是运行速度,都是目前开源的短语结构分析器中最好的。其语法树如下图:
普通句法树与PCFG-LA句法树对照实例
这个x就是隐含标记,xi的取值范围一般是人为设定的,一般取1~16之间的整数。而且PCFG-LA也类似于HMM模型,原始非终结符对应HMM模型中的观察输出,而隐含标记对应HMM模型中的隐含状态。
浅层语法分析(局部语法分析)
由于完全语法分析要确定句子所包含的全部句法信息,并确定句子中各成分之间的关系,这是一项十分苦难的任务。到目前为止,句法分析器的各方面都难以达到令人满意的程度,为了降低问题的复杂度,同时获得一定的句法结构信息,浅层句法分析应运而生。
浅层语法分析只要求识别句子中的某些结构相对简单的独立成为,例如非递归的名词短语、动词短语等,这些被识别出来的结构通常称为语块(chunk)。
浅层句法分析将句法分析分解为两个主要子任务,一个是语块的识别和分析,另一个是语块之间的依附关系分析。其中,语块的识别和分析是主要任务。在某种程度上说,浅层句法分析使句法分析的任务得到了简化,同时也有利于句法分析系统在大规模真实文本处理系统中迅速得到应用。
基本名词短语(base NP)是语块中的一个重要类别,它指的是简单的、非嵌套的名词短语,不含有其他子项短语,并且base NP之间结构上是独立的。示例如下:
base NP识别就是从句子中识别出所有的base NP,根据这种理解,一个句子中的成分和简单的分为baseNP和非base NP两类,那么base NP识别就成了一个分类问题。
base NP的表示方法有两种,一种是括号分隔法,一种是IOB标注法。括号分隔法就是将base NP用方括号界定边界,内部的是base NP,外部的不属于base NP。IOB标注法中,字母B表示base NP的开端,I表示当前词语在base NP内,O表示词语位于base NP之外。
基于SVM的base NP识别方法
由于base NP识别是多值分类问题,而基础SVM算法解决的是二值分类问题,所以一般可以采用配对策略(pairwise method)和一比其余策略(one vs. other method)。
SVM一般要从上下文的词、词性、base NP标志中提取特征来完成判断。一般使用的词语窗口的长度为5(当前词及其前后各两个词)时识别的效果最好。
基于WINNOW的base NP识别方法
WINNOW是解决二分问题的错误驱动的机器学习方法,该方法能从大量不相关的特征中快速学习。
WINNOW的稀疏网络(SNoW)学习结构是一种多类分类器,专门用于处理特征识别领域的大规模学习任务。WINNOW算法具有处理高维度独立特征空间的能力,而在自然语言处理中的特征向量恰好具有这种特点,因此WINNOW算法也常用于词性标注、拼写错误检查和文本分类等等。
简单WINNOW的基本思想是,已知特征向量和参数向量和实数阈值θ,先将参数向量均初始化为1,将训练样本代入,求特征向量和参数向量的内积,将其与θ比较,如果大于θ,则判定为正例,小于θ则判定为反例,将结果与正确答案作比较,依据结果来改变权值。
如果将正例估计成了反例,那么对于原来值为1的x,把它的权值扩大。如果将反例估计成了正例,那么对于原来值为1的x,把它的权值缩小。然后重新估计重新更改权重,直到训练完成。
这其实让我想到了LR算法,因为LR算法也是特征向量与参数向量的内积,最后将其送到Sigmoid函数中去拿到判定结果,然后大于0.5的为正例,小于0.5的为反例,实际上只要反过来,Sigmod函数输出0.5时候的输入就是WINNOW算法里的那个实数阈值θ。但是区别在于WINNOW算法只判定大小,不判定概率,而LR利用Sigmoid函数给出了概率。LR利用这给出的概率,通过使训练集的生成概率最大化来调整参数,而WINNOW则是直接朴素的错误情况来增大或缩小相关参数。目测LR因为使用了梯度下降,它的收敛速度要快于WINNOW,而WINNOW的优势则在于可以处理大量特征。
基于CRF的base NP识别方法
基于CRF的base NP识别方法拥有与SVM方法几乎一样的效果,优于基于WINNOW的识别方法、基于MEMM的识别方法和感知机方法,而且基于CRF的base NP识别方法在运行速度上较其他方法具有明显优势。
依存语法理论
在自然语言处理中,我们有时不需要或者不仅仅需要整个句子的短语结构树,而且要知道句子中 词与词之间的依存关系 。用词与词之间的依存关系来描述语言结构的框架成为依存语法,又称从属关系语法。利用依存语法进行句法分析也是自然语言理解的重要手段之一。
有人认为,一切结构语法现象可以概括为关联、组合和转位这三大核心。句法关联建立起词与词之间的从属关系,这种从属关系由 支配词 和 从属词 联结而成, 谓语中的动词是句子的中心并支配别的成分,它本身不受其他任何成分支配 。
依存语法的本质是一种结构语法,它主要研究以谓词为中心而构句时由深层语义结构映现为表层语法结构的状况及条件,谓词与体词之间的同现关系,并据此划分谓词的词类。
常用的依存于法结构图示有三种:
计算机语言学家J. Robinson提出了依存语法的四条公理:
一个句子只有一个独立的成分
句子的其他成分都从属于某一成分
任何一个成分都不能依存于两个或两个以上的成分
如果成分A直接从属于成分B,而成分C在句子中位于A和B之间,那么,成分C或者属于成分A,或者从属于B,或者从属于A和B之间的某一成分。
这四条公理相当于对依存图和依存树的形式约束:单一父节点、连通、无环和可投射,由此来保证句子的依存分析结果是一棵有根的树结构。
这里提一下可投射,如果单词之间的依存弧画出来没有任何的交叉,就是可投射的(参考上面的两个有向图)。
为了便于理解,我国学者提出了依存结构树应满足的5个条件:
单纯结点条件:只有终结点,没有非终结点
单一父结点条件:除根节点没有父结点外,所有的结点都只有一个父结点
独根结点条件:一个依存树只能有一个根结点,它支配其他结点
非交条件:依存树的树枝不能彼此相交
互斥条件:从上到下的支配关系和从左到右的前于关系之间是相互排斥的,如果两个结点之间存在着支配关系,它们就不能存在于前于关系
这五个条件是有交集的,但它们完全从依存表达的空间结构出发,比四条公理更直观更实用。
Gaifman 1965年给出了依存语法的形式化表示,证明了依存语法与上下文无关文法没有什么不同..
类似于上下文无关文法的语言形式对被分析的语言的投射性进行了限制,很难直接处理包含非投射现象的自由语序的语言。20世纪90年代发展起来了约束语法和相应的基于约束满足的依存分析方法,可以处理此类非投射性语言问题。
基于约束满足的分析方法建立在约束依存语法之上,将依存句法分析看做可以用约束满足问题来描述的有限构造问题。
约束依存语法用一系列形式化、描述性的约束将不符合约束的依存分析去掉,直到留下一棵合法的依存树。
生成式依存分析方法、判别式依存分析方法和确定性依存分析方法是数据驱动的统计依存分析中具有代表性的三种方法。
生成性依存分析方法
生成式依存分析方法采用联合概率模型生成一系列依存语法树并赋予其概率分值,然后采用相关算法找到概率打分最高的分析结果作为最后输出。
生成式依存分析模型使用起来比较方便,它的参数训练时只在训练集中寻找相关成分的计数,计算出先验概率。但是,生成式方法采用联合概率模型,再进行概率乘积分解时做了近似性假设和估计,而且,由于采用全局搜索,算法的复杂度较高,因此效率较低,但此类算法在准确率上有一定优势。但是类似于CYK算法的推理方法使得此类模型不易处理非投射性问题。
判别式依存分析方法
判别式依存分析方法采用条件概率模型,避开了联合概率模型所要求的独立性假设(考虑判别模型CRF舍弃了生成模型HMM的独立性假设),训练过程即寻找使目标函数(训练样本生成概率)最大的参数θ(类似Logistic回归和CRF)。
判别式方法不仅在推理时进行穷尽搜索,而且在训练算法上也具有全局最优性,需要在训练实例上重复句法分析过程来迭代参数,训练过程也是推理过程,训练和分析的时间复杂度一致。
确定性依存方法
确定性依存分析方法以特定的方向逐次取一个待分析的词,为每次输入的词产生一个单一的分析结果,直至序列的最后一个词。
这类算法在每一步的分析中都要根据当前分析状态做出决策(如判断其是否与前一个词发生依存关系),因此,这种方法又称决策式分析方法。
通过一个确定的分析动作序列来得到一个唯一的句法表达,即依存图(有时可能会有回溯和修补),这是确定性句法分析方法的基本思想。
短语结构与依存结构之间的关系
短语结构树可以被一一对应地转换成依存关系树,反之则不然。因为一棵依存关系树可能会对应多棵短语结构树。
比较长 不过支持全部的关键字 直接就可以用了 using Systemusing System Textusing System Text RegularExpressionsnamespace Com OSLeague Component{/// <summary>/// 语法分析器 将所有Code根据语法进行变色/// <list type= VB >支持VB NET</list>/// <list type= CS >支持CS</list>/// <author>掉掉</author>/// <date>年 月 日锋亩</date>/// <Memo>/// 练习正则表达式/// </Memo>/// </summary>public class CodeAnalysis{
////定义HTML开始和结束的银轿森语句 用于语法变色//
const string TAG_FNTRED = @ <font color= red >const string TAG_FNTBLUE = @ <font color= blue >const string TAG_FNTGRN = @ <font color= green >const string TAG_FNTMRN = @ <font color= maroon >const string TAG_FNTBLACK = @ <font color= black >const string TAG_EFONT = @ </font>const string TAG_SPNYELLOW = @ <span style= background color: yellow>const string TAG_ESPAN = @ </span>const string TAG_B = @ <b>const string TAG_EB = @ </b>const string TAG_MENT = @ <font colr=# >const string TAG_EMENT = @ </font>
//
public CodeAnalysis(){//// TODO: 在此处添加构造函数逻辑//}
/// <summary>/// 处理VB NET代码 彩色化 /// </summary>/// <param name= Code >传入的Code</param>/// <returns>处理过后的代码</returns>public string ParseVB(string Code){////定义VB NET中关键字 将其存为帆慎数组//
string[] VB_Keyword = new string[]{ AddHandler AddressOf AndAlso Alias And Ansi As Assembly Auto Boolean ByRef Byte ByVal Call Case Catch CBool CByte CChar CDate CDec CDbl Char CInt Class CLng CObj Const CShort CSng CStr CType Date Decimal Declare Default Delegate Dim DirectCast Do Double Each Else ElseIf End Enum Erase Error Event Exit False Finally For Friend Function Get GetType GoTo Handles If Implements Imports In Inherits Integer Interface Is Let Lib Like Long Loop Me Mod Module MustInherit MustOverride MyBase MyClass Namespace New Next Not Nothing NotInheritable NotOverridable Object On Option Optional Or OrElse Overloads Overridable Overrides ParamArray Preserve Private Property Protected Public RaiseEvent ReadOnly ReDim RemoveHandler Resume Return Select Set Shadows Shared Short Single Static Step Stop String Structure Sub SyncLock Then Throw To True Try TypeOf Unicode Until Variant When While With WithEvents WriteOnly Xor }
////设定转换代码颜色//
lishixinzhi/Article/program/net/201311/14615目 录
1 课程任务书····································(2)
1问题描述·······································(3)
2文法及属性文法的描述···························(3)
2.1 while-do循环语句的文法·····················(3)
2.2while-do循环语句的结构翻译·················(3)
3语法分析及中间代码形式的描述···················(4)
3.1 语法分析方法·······························(4)
3.2 中间代码形式描述···························(4)
4简要的分析与概要设计···························(5)
4.1词法分析··································(5)
4.2递归下降翻译器的设计·······················(5)
4.3语法制导翻译·······························(5)
5 详细的算法描述································(6)
5.1 文法·······································(6)
5.2 查错·······································(6)
6 测试方法和测试结果···························(9)
6.1测试方法··································(9)
6.2测试结果··································(10)
7 设计的特点、不足、收获与体会·················(10)
7.1 设计的特点································(10)
7.2 不足、收获与体会··························(11)
8 参考文献·····································(11)
课程设计任务书
题 目: 循环语句的语法分析及伍岁此语义分析程序设计(递归下降法)
1.目的
通过设计、编制、调试一个语法及语义分析程序,加深对语法及语义分析原理的理解。
2.设计内容及要求
WHILE〈布尔表达式〉DO〈赋值语句〉
其中
(1)学号29至32的同学按顺序分别选择递归下降法、LL(1)、算符优先分析法(或简单优先法)、LR法完成以上任务,中间代码选用四元式。
(2)如1题写出符合分析方法要求的文法,给出分析方法的思想,完成分析程序设计。
(3)编制好分析程序后,设计若干用例,上机测试并通过所设计的分析程序。
3.课程设计报告书的内容应包括:
1.设计题目、班级、学号、姓名、完成日期;
2.给出语法分析方法及中间代码形式腔迅的描述、文法和属性文法的设计;或者词法分析方法
3.及符号表和TOKEN代码的设计。
4.简要的分析与概要设计;
5.详细的算法描述;
6.源程序清单;
7.给出软件的测试方法和测试结果;
8.设计的评价、收获与体会。
4.时间安排:
第17周,周1-周4上午,周五全天
指导教师签名: 年月日
系主任(或责任教师)签名: 年月日
1问题描述
设计一个WHILE〈布尔表达式〉DO〈赋值语句〉雀岩循环语句的词法﹑语法及语义分析程序,语法分析选择递归下降法,采用用语法制导翻译输出中间代码四元式。
2文法及属性文法的描述。
2.1 while-do循环语句的文法
产生式为S->while E do A,为便于语法制导翻译将其改写如下:
文法G(s)如下:
S-->WEDG (意思是while E do G)
G-->c=R
R-->dTe|d
T-->+|-|*|/
E-->aFb
F-->>|==|<
2.2 whlie-do循环语句的结构翻译:
3.语法分析方法及中间代码形式的描述
3.1语法分析方法
递归下降法的实现思想是为文法的每个非终结符号设计一个相对应的递归子程序,识别程序由一组这样的子程序组成。
它的优点是简单直观,易于构造,很多编译系统所实现
缺点是对文法要求很高,由于递归调用多,影响分析器的效率
其文法可以表示为:
E→T│E+T
T→F│T*F
F→i│(E)
可以用语法图来表示语言的文法,如图:
E
T
F
3.2中间代码形式描述
中间代码采用四元式输出,一个四元式是一个带有四个域的记录结构,这四个域分别称为op﹑arg1﹑arg2及result。域op包含一个代表运算符的内部码。语句while a<b do a=a+b的四元式输出形式如下:
100 ( <, a , b , 102 )
101 ( j , _ , _ , 105 )
102 ( + , a , b , n )
103 ( = , n , _ , a )
104 ( j , _ , _ , 100)
105
4.简要的分析与概要设计
4.1词法分析
词法分析程序的任务是:从左至右逐个字符地对源程序进行扫描,产生一个个的单词符号,把作为字符串的源程序改造成为单词符号的中间程序。词法分析检查的错误主要是挑出源程序中出现的非法符号。所谓非法符号是指不是程序设计语言中允许出现的符号,就像自然语句中的错字。
4.2递归下降翻译器的设计
1.:对每个非终结符A构造一个函数过程,对A的每个继承属性设置一个形式参数,函数的返回值为A的综合属性,A对应的函数过程中,为出现在A的产生式中的每一个文法符号的每一个属性都设置一个局部变量。非终结符A对应的函数过程中,根据当前的输入符号决定使用哪个产生式候选。
2:每个产生式对应的程序代码中,按照从左到右的次序,对于单词符号,非3:终结符和语义动作分别做以下工作。
(1)对于带有综合属性x的终结符X,把x的值存入为X,x设置的变量中。然后产生一个匹配X的调用,并继续读入一个输入符号。
(2)对于每个非终结符号B,产生一个右边带有函数调用的赋值语句c=B(b1,b2,…,bk)
(3)对于语义动作,把动作的代码抄进分析器中,用代表属性的变量来代替对应属性的每一次引用。
4.3语法制导翻译
在语法分析过程中,随着分析的步步进展,根据每个产生式所对应的语义子程序(或语义规则描述的语义动作)进行翻译。属性文法的每个符号有属性,所以每个符号入栈时,必须连属性一起入栈,这样,栈符号就由文法符号及存放该符号属性的域所组成。由于属性类型不同,属性域存放的内容就要根据属性的类型来定。有的可能直接存放属性值,也有的存放的是指向属性值的指针。对于综合属性,其属性域不存放其属性值,而是存放一个指针,指向存贮该属性值的单元。对于继承属性,其属性域直接保存其属性值。继承属性的属性域刚入栈时为空,但是在该栈符号变成栈顶符号之前的某一时刻,它们必须接受相应的属性值,即在成为栈顶时,继承属性的属性域必须有值。
5详细的算法描述
5.1 文法
/*
文法G(s)
s-->WEDG
G-->c=R
R-->dTe|d
T ->+|-|*|/|%E-->aFb
F-->>|==|<
*/
5.2 查错
按照递归下降法求Wa<bDa=a+b,程序的执行顺序应该是S()W()EF()D()G()R()T()
S()
void S()
{
printf("%d\tS-->WEDG\n",total)total++
W()
E()
}
W()
void W()
{
if(ch!='W')
{
printf("有非法字符%c请按回车返回!!",ch)
getchar()
getchar()
exit(1)
}
}
E()
void E()
{
ch=a[++i1]
if(ch!='a')
{
printf("有非法字符%c %c请按回车返回!!",ch)
getchar()
getchar()
exit(1)
}
printf("%d\tE-->aFb\n",total)total++
F()
}
F()
void F()
{
int i
ch=a[++i1]
i=i1+1
if(a[i]!='b')
{
printf("有非法字符%c请按回车返回!!",a[i])
getchar()
getchar()
exit(1)
}
switch(ch)
{
case '>':
printf("%d\tF-->>\n",total)total++
break
case '==':
printf("%d\tF-->==\n",total)total++
break
default:
printf("%d\tF--><\n",total)total++
break
}
D()
G()
}
D()
void D()
{
++i1
ch=a[++i1]
if(ch!='D')
{
printf("有非法字符%c请按回车返回!!",ch)
getchar()
getchar()
exit(1)}
ch=a[++i1]
}
G()
void G()
{
int i=i1+1
if(ch!='c'&&a[i]!='=')
{
printf("有非法字符%c %c请按回车返回!!",ch,a[i])
getchar()
getchar()
exit(1)
}
printf("%d\tG-->c=R\n",total)total++
R()
}
R()
void R()
{
int i
i=i1+1
i1=i1+2
ch=a[i1]
if(a[i]!='='&&ch!='d')
{
printf("有非法字符%c %c请按回车返回!!",a[i],ch)
getchar()
getchar()
exit(1)
}
else
{
if((a[i1+1]=='+')||(a[i1+1]=='-')||(a[i1+1]=='*')||(a[i1+1]=='/'))
{
printf("%d\tR-->dTe\n",total)total++
T()
}
else
{
printf("%d\tR-->d\n",total)total++
W()
E()
}
}
}
T()
void T()
{
ch=a[++i1]
switch(ch)
{
case '+':
printf("%d\tT-->+\n",total)total++
break
case '-':
printf("%d\tT-->-\n",total)total++
break
case '*':
printf("%d\tT-->*\n",total)total++
break
default:
printf("%d\tT-->/\n",total)total++
break
}
ch='#'
}
6测试方法和测试结果
6.1测试方法
在C++环境下,设计几个有代表的用例,进行测试,例如:输入语句Wa<bDa=a+b#(其中d表示do ,w表示while)。若得出的不是预期的结果,那么程序就出现问题。如果有问题的话就进行单步调试找到程序中出现的逻辑问题。
6.2测试结果
测试结果如下:
7设计的特点、不足、收获与体会
7.1设计的特点
本次设计是采用递归下降的方法对输入的while--do 循环语句进行语法,语义分析,并输出四元式。因此程序中充分体现了递归下降的思想。
7.2设计的不足,收获与体会
本次的设计的不足主要是我没将程序一般化,实现不了用户自动输入代码进行词法分析的四元式输出,此程序只能实现对Wa<bDa=a+b#的分析与四元式输出,由于我所设计的栈中只能一个字符一个字符的存放,因此只能用D W分别表示do while;而且我对语法制导翻译这一块很不熟悉,因此我始终不能用程序实现语法制导翻译输出四元式,于是根据自己的理解,直接把四元式写了出来。
本次课程设计巩固了我所学习的关于递归下降法这一方面的知识,并且使我对WHILE—DO循环语句也有了更深刻的理解,提高了我的动手能力。
8 课程设计参考资料
1张幸儿 《编译原理》(第二版)清华大学出版社
2何炎祥 《编译原理》华中理工大学出版社
3陈火旺 《程序设计语言编译原理》(第3版)国防工业出版社
本科生课程设计成绩评定表
班级:软件0701 姓名:周璐萍 学号:0120710680129
序号 评分项目 满分 实得分
1 学习态度认真、遵守纪律 10
2 设计分析合理性 10
3 设计方案正确性、可行性、创造性 20
4 设计结果正确性 40
5 设计报告的规范性 10
6 设计验收 10
总得分/等级
评语:
注:最终成绩以五级分制记。优(90-100分)、良(80-89分)、中(70-79分)、
及格(60-69分)、60分以下为不及格
源程序
#include <stdio.h>
#include<dos.h>
#include<stdlib.h>
#include<string.h>
char a[50],g[50][50]
char ch
int n1,i1=0,i2=0
int total=0
void S()
void D()
void G()
void W()
void E()
void R()
void T()
void F()
void main()
{
int j=0
printf("文法G(s)为:\n")
printf("s-->DGWE\n")
printf("G-->c=R\n")
printf("R-->dTe|d\n")
printf("T-->+|-|*|/\n")
printf("E-->aFb\n")
printf("F-->>|==|<\n")
printf("请输入while-do语句(D代表do,W代表while),并以#结束:\n")
do{
scanf("%c",&ch)
a[j]=ch
j++
}while(ch!='#')
n1=j
ch=a[0]
S()
printf("\n")
if (ch=='#')
{ printf("输出四元式为:\n")
printf("100 (<,a,b,102)\n")
printf("101 (j,_,_,105)\n")
printf("102 (+,a,b,n)\n")
printf("103 (=,n,_,a)\n")
printf("104 (j,_,_,100)\n")
printf("105\n")
}
else {
printf("error\n")
printf("press any key to continue..\n")
getchar()getchar()
return
}
printf("\n")
printf("press any key to continue..\n")
getchar()
getchar()
}
/*出错情况分析*/
void S()
{
printf("%d\tS-->WEDG\n",total)total++
W()
E()
}
void W()
{
if(ch!='W')
{
printf("有非法字符%c请按回车返回!!",ch)
getchar()
getchar()
exit(1)
}
}
void E()
{
ch=a[++i1]
if(ch!='a')
{
printf("有非法字符%c %c请按回车返回!!",ch)
getchar()
getchar()
exit(1)
}
printf("%d\tE-->aFb\n",total)total++
F()
}
void F()
{
int i
ch=a[++i1]
i=i1+1
if(a[i]!='b')
{
printf("有非法字符%c请按回车返回!!",a[i])
getchar()
getchar()
exit(1)
}
switch(ch)
{
case '>':
printf("%d\tF-->>\n",total)total++
break
case '==':
printf("%d\tF-->==\n",total)total++
break
default:
printf("%d\tF--><\n",total)total++
break
}
D()
G()
}
void D()
{ ++i1
ch=a[++i1]
if(ch!='D')
{ printf("有非法字符%c请按回车返回!!",ch)
getchar()
getchar()
exit(1)}
ch=a[++i1]
}
void G()
{ int i=i1+1
if(ch!='c'&&a[i]!='=')
{ printf("有非法字符%c %c请按回车返回!!",ch,a[i])
getchar()
getchar()
exit(1)}
printf("%d\tG-->c=R\n",total)total++
R()
}
void R()
{
int i
i=i1+1
i1=i1+2
ch=a[i1]
if(a[i]!='='&&ch!='d')
{
printf("有非法字符%c %c请按回车返回!!",a[i],ch)
getchar()
getchar()
exit(1)
}
else
{
if((a[i1+1]=='+')||(a[i1+1]=='-')||(a[i1+1]=='*')||(a[i1+1]=='/'))
{
printf("%d\tR-->dTe\n",total)total++
T()
}
else
{
printf("%d\tR-->d\n",total)total++
W()
E()
}
}
}
void T()
{
ch=a[++i1]
switch(ch)
{
case '+':
printf("%d\tT-->+\n",total)total++
break
case '-':
printf("%d\tT-->-\n",total)total++
break
case '*':
printf("%d\tT-->*\n",total)total++
break
default:
printf("%d\tT-->/\n",total)total++
break
}
ch='#'
}
指导教师签名:
2010 年 月 日
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)