编译原理, 算符优先文法代码分析方法是依据文法计算出 算符优先文法代码关系,再将其构造成语法分析程序需要使用的。

编译原理课程设计--基于算符优先分析方法的表达式语法分析器_百度文库
两大类热门资源免费畅读
续费一年阅读会员,立省24元!
编译原理课程设计--基于算符优先分析方法的表达式语法分析器
阅读已结束,下载本文需要
想免费下载本文?
定制HR最喜欢的简历
下载文档到电脑,同时保存到云知识,更方便管理
加入VIP
还剩31页未读,
定制HR最喜欢的简历
你可能喜欢没有更多推荐了,
不良信息举报
举报内容:
编译原理实验之语法分析(算符优先分析算法(C语言))
举报原因:
原文地址:
原因补充:
最多只允许输入30个字
加入CSDN,享受更精准的内容推荐,与500万程序员共同成长!硕士/研究生
&&&&&&DOC文档下载
游客快捷下载
会员登录下载
下载资源需要20元
邮箱/手机号:
您支付成功后,系统会自动为您创建此邮箱/手机号的账号,密码跟您输入的邮箱/手机号一致,以方便您下次登录下载和查看订单。
支付方式:
已注册用户请登录:
当日自动登录&&
&&合作网站一键登录:
1、本站资源不支持迅雷下载,请使用浏览器直接下载(不支持QQ浏览器);
2、文档下载后都不会有金锄头文库的水印,预览文档经过压缩,下载后原文更清晰;
3、所有的PPT和DOC文档都被视为“模板”,允许上传人保留章节、目录结构的情况下删减部份的内容;下载前须认真查看,确认无误后再购买;
4、所有文档都是可以预览的,金锄头文库作为内容存储提供商,无法对各卖家所售文档的真实性、完整性、准确性以及专业性等问题提供审核和保证,请慎重购买;
5、文档的总页数、文档格式和文档大小以系统显示为准(内容中显示的页数不一定正确),网站客服只以系统显示的页数、文件格式、文档大小作为仲裁依据;
6、如果您还有什么不清楚的,可以点击右侧栏的客服对话;
下载须知 | 常见问题汇总
编译原理 第六章 算符优先分析法
第六章算符优先分析法课前索引【课前思考】◇什么是自下而上语法分析的策略◇什么是移进归约分析◇移进归约过程和自顶向下最右推导有何关系◇自下而上语法分析成功的标志是什么◇什么是可归约串◇移进归约过程的关键问题是什么◇如何确定可归约串◇如何决定什么时候移进,什么时候归约◇什么是算符文法什么是算符优先文法◇算符优先分析是如何识别可归约串的◇算符优先分析法的优缺点和局限性有哪些【学习目标】算符优先分析法是自下而上(自底向上)语法分析的一种,尤其适应于表达式的语法分析,由于它的算法简单直观易于理解,因此,也是学习其它自下而上语法分析的基础。通过本章学习学员应掌握◇对给定的文法能够判断该文法是否是算符文法◇对给定的算符文法能够判断该文法是否是算符优先文法◇对给定的算符文法能构造算符优先关系表,并能利用算符优先关系表判断该文法是否是算符优先文法。◇能应用算符优先分析算法对给定的输入串进行移进归约分析,在分析的每一步能确定当前应移进还是归约,并能判断所给的输入串是否是该文法的句子。◇了解算符优先分析法的优缺点和实际应用中的局限性。【学习指南】算符优先分析法是自下而上语法分析的一种,它的算法简单、直观、易于理解,所以通常作为学习其它自下而上语法分析的基础。为学好本章内容,学员应复习有关语法分析的知识,如什么是语言、文法、句子、句型、短语、简单短语、句柄、最右推导、规范归约基本概念。【难重点】◇通过本章学习后,学员应该能知道算符文法的形式。◇对一个给定的算符文法能构造算符优先关系分析表,并能判别所给文法是否为算符优先文法。◇分清规范句型的句柄和最左素短语的区别,进而分清算符优先归约和规范归约的区别。◇算符优先分析的可归约串是句型的最左素短语,在分析过程中如何寻找可归约串是算符优先分析的关键问题。对一个给定的输入串能应用算符优先关系分析表给出分析(归约)步骤,并最终判断所给输入串是否为该文法的句子。◇深入理解算符优先分析法的优缺点和实际应用中的局限性。【知识点】第6章自底向上优先分析短语、直接短语、句柄的定义文法GS,SΑAΔ且AΒ则称Β是句型ΑΒΔ相对于非终结符A的短语。若有AΒ则称Β是句型ΑΒΔ相对于A或规则A→Β的直接短语。一个句型的最左直接短语称为该句型的句柄。算符优先分析法是一种自底向上分析方法,也称移进归约分析法,粗略地说它的实现思想是对输入符号串自左向右进行扫描,并将输入符逐个移入一个后进先出栈中,边移入边分析,一旦栈顶符号串形成某个句型的句柄时,该句柄对应某产生式的右部,就用该产生式的左部非终结符代替相应右部的文法符号串,这称为一步归约。重复这一过程直到归约到栈中只剩文法的开始符号时则为分析成功,也就确认输入串是文法的句子。本章将在介绍自底向上分析思想基础上,着重介绍算符优先分析法。栈顶符号串是指该符号串包含栈顶符号在内,并由栈中某个符号为该符号串的头,栈顶符号为该符号串的尾,也可以只有一个栈顶符号。61自底向上分析概述自底向上分析法,也称移进归约分析法,或自下而上分析。现举例说明。例61设文法GS为1S→AACBE2A→B3A→AB4B→D对输入串ABBCDE进行分析,检查该符号串是否是GS的句子。由于自底向上分析的移进归约过程是自顶向下最右推导的逆过程,而最右推导为规范推导,自左向右的归约过程也称规范归约。容易看出对输入串ABBCDE的最右推导是SAACBEAACDEAABCDEABBCDE由此我们可以构造它的逆过程即归约过程。先设一个先进后出的符号栈,并把句子左括号号放入栈底,其分析过程如表61。表61用移进归约对输入串ABBCDE的分析过程F611SWF图61自底向上构造语法树的过程推倒的逆过程为SAACBEAACDEAABCDEABBCDE对上述分析过程也可看成自底向上构造语法树的过程,每步归约都是构造一棵子树,最后当输入串结束时刚好构造出整个语法树,图61ABCD给出构造过程,可与表中相应分析步骤对照。在上述移进归约或自底向上构造语法树的过程中,我们怎么知道何时移进,何时归约,不能只简单地看成栈顶出现某一产生式的右部就可用相应产生式归约,例如在表61分析到第5步时栈中的符号串是AAB,栈顶符号串B和AB分别是产生式2,3的右部,这时到底用2归约还是用3归约是自底向上分析要解决的关键问题。由于移进归约是规范推导最右推导的逆过程,即规范归约最左归约。当一个文法无二义性时,那么它对一个句子的规范推导是唯一的,规范归约也必然是唯一的。因而每次归约时一定要按当前句型的句柄,也就是说,任何时候栈中的符号串和剩余的输入串组成一个句型,当句柄出现在栈顶符号串中时,则可用句柄归约,这样一直归约到输入串只剩结束符,文法符号栈中只剩开始符号。这时才能认为输入符号串是文法的句子。否则为出错。由此可见自底向上分析的关键问题是在分析过程中如何确定句柄,也就是说如何知道何时在栈顶符号串中已形成某句型的句柄,那么就可以确定何时可以进行归约。自底向上的分析算法很多,我们仅在本章和第7章介绍目前常用的算符优先分析和LR类分析法。小练习用PL/0的READA语句为例构造自底向上的语法分析树,以体会自底向上的归约过程。下面为参考答案。图63自底向上的语法分析第6章自底向上优先分析62算符优先分析法算符优先分析的基本思想是只规定算符广义为终结符之间的优先关系,也就是只考虑终结符之间的优先关系,不考虑非终结符之间的优先关系。在归约过程中只要找到可归约串就归约,并不考虑归约到那个非终结符名,算符优先分析的可归约串不一定是规范句型的句柄,所以算符优先归约不是规范归约。算符优先分析的可归约串是当前符号栈中的符号和剩余的输入符号构成句型的最左素短语。例如若有文法G为1E→EE2E→EE3E→I对输入串I1I2I3的归约过程可表示为表63。表63对输入串I1I2I3的归约过程F622SWF在分析到第6步时,栈顶的符号串为EE,若只从移进归约的角度讲,栈顶已出现了产生式1的右部,可以进行归约,但从通常四则运算的习惯来看应先乘后加,所以应移进,这就提出了算符优先的问题。本节所给例子是二义性文法,对二义性文法不能构造确定的分析过程,但是在本例中,人为地规定了算符之间的优先关系,仍然可构造出确定的分析过程。621直观算符优先分析法通常在算术表达式求值过程中,运算次序是先乘除后加减,这说明了乘除运算的优先级高于加减运算的优先级,乘除为同一优先级但运算符在前边的先做,这称为左结合,同样加减运算也是如此,这也说明了运算的次序只与运算符有关,而与运算对象无关,因而直观算符优先分析法的关键是对一个给定文法G,人为地规定其算符的优先顺序,即给出优先级别和同一个级别中的结合性质,算符间的优先关系表示规定如下AB表示A的优先性低于B。AB表示A的优先性等于B,即与B相同。AB表示A的优先性高于B。但必须注意,这三个关系和数学中的<,=,>是不同的。当有关系AB时,却不一定有关系BA,当有关系AB时,却不一定有BA,例如通常表达式中运算符的优先关系有,但没有,有,但没有。下面给出一个表达式的文法为E→EE|E-E|EE|E/E|E↑E|E|I本文法是二义性的,由于人为地规定了算符之间的优先级别和同一个级别中的结合性质,所以可能构造出确定的分析过程。我们可以对此表达式的文法按公认的计算顺序规定优先级和结合性如下①↑优先级最高。遵循右结合,相当↑↑。第6章自底向上优先分析例如2↑3↑22↑9512。(而若为左结合则2↑3↑28↑264)也就是同类运算符在归约时为从右向左归约。即I1↑I2↑I3式先归约I2↑I3。②,/优先级其次。服从左结合,相当、/、//、/。③,优先级最低。服从左结合,相当、、、。④对,规定括号的优先性大于括号外的运算符,小于括号内的运算符,内括号的优先性大于外括号。对于句子括号号规定与它相邻的任何运算符的优先性都比它大。此外,对运算对象的终结符I其优先级最高。综上所述,我们可对表达式运算符的优先关系构造如表64。表64算符优先关系表/↑I/↑I很显然所给表达式文法是二义性的,但我们人为直观地给出运算符之间的优先关系,由优先关系表64可知这种优先关系是唯一的,有了这个优先关系表我们对前面表达式的输入串I1I2I3归约过程就能唯一确定了,也就是说在表63分析到第6)步时,栈中出现了EE,可归约为E,但当前输入符为,由于规定,所以应移进。本节简单介绍直观算符优先分析法,仅仅是为了易于理解算符优先分析法的概念,下一节将介绍对任意给定的一个文法如何按形式算法的规则计算算符之间的优先关系。622算符优先文法的定义我们首先给出算符文法和算符优先文法的定义。定义61设有一文法G,如果G中没有形如A→BC的产生式,其中B和C为非终结符,则称G为算符文法OPERATERGRAMMAR也称OG文法。例如表达式文法E→EE|EE|E|I其中任何一个产生式中都不包含两个非终结符相邻的情况,因此该文法是算符文法。小练习学员可证明算符文法有如下两个性质。性质1在算符文法中任何句型都不包含两个相邻的非终结符。性质2如果AB或BA出现在算符文法的句型Γ中,其中A∈VN,B∈VT,则Γ中任何含B的短语必含有A。参考答案证明性质1用归纳法设Γ是句型,即SΓSΩ0Ω1ΩN1ΩNΓ推导长度为N,归纳起点N1时,SΩ0Ω1Γ,即SΓ,必存在产生式S→Γ,而由算符文法的定义,文法的产生式中无相邻的非终结符,显然满足性质1。假设N>1,ΩN1满足性质1。若ΩN1ΑAΔ,A为非终结符。由假设Α的尾符号和Δ的首符号都不可能是非终结符,否则与假设矛盾。又若A→Β是文法的产生式,则有ΩN1ΩNΑΒΔΓ而A→Β是文法的原产生式,不含两个相邻的非终结符,所以ΑΒΓ也不含两个相邻的非终结符。满足性质1。证毕。证明性质2用反证法。因为由算符文法的性质1知可有SΓΑBAΒ若存在BΑB,这时B和A不同时归约,则必有SBAΒ,这样在句型BAΒ中存在相邻的非终结符B和A,所以与性质1矛盾,证毕。注意含B的短语必含A,含A的短语不一定含B。定义62设G是一个不含Ε产生式的算符文法,A和B是任意两个终结符,A、B、C是非终结符,算符优先关系、、定义如下①AB当且仅当G中含有形如A→AB或A→ABB的产生式②AB当且仅当G中含有形如A→AB的产生式,且BB或BCB③AB当且仅当G中含有形如A→BB的产生式,且BA或BAC以上三种关系也可由下列语法树来说明①AB则存在语法子树如图63A其中Δ为Ε或为B,这样
本文(编译原理 第六章 算符优先分析法)为本站会员(sanshengyuan)主动上传,金锄头文库仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。
若此文所含内容侵犯了您的版权或隐私,请立即阅读金锄头文库的“”【网址:】,按提示上传提交保证函及证明材料,经审查核实后我们立即给予删除!
温馨提示:如果因为网速或其他原因下载失败请重新下载,重复下载不扣分。
分享当前资源【编译原理 第六章 算符优先分析法】到朋友圈,您即可以免费下载此资源!
微信扫一扫分享到朋友圈
操作提示:任选上面一个二维码,打开微信,点击“发现”使用“扫一扫”,即可将选择的网页分享到朋友圈
您可能感兴趣的------------------------------------------------------------------------------------------------------
元price_share
&|&川公网安备 12号&|&经营许可证(蜀ICP备号-1)(C) by Sichuan Goldhoe Inc. All Rights Reserved.
&strong>&span style=&font-family: 微软雅黑, &Microsoft YaHei&;&>一、&/span>&/strong>&span style=&font-family: 微软雅黑, &Microsoft YaHei&;&>本站提供全自助服务,购买后点击下载按钮可以下载到你电脑或手机(系统不会发送文档到您的邮箱),请注意查看下载存放位置;&/span>&/p>&p>&strong>&span style=&font-family: 微软雅黑, &Microsoft YaHei&;&>二、&/span>&/strong>&span style=&font-family: 微软雅黑, &Microsoft YaHei&;&>本站具有防盗链功能,所以不要使用迅雷、旋风、网际快车等第三方辅助下载工具(不支持&span style=&font-family: 微软雅黑, &Microsoft YaHei&;&>QQ浏览器&/span>),否则下载下来的文件只是网页或乱码;&/span>&br/>&/p>&p>&strong>&span style=&font-family: 微软雅黑, &Microsoft YaHei&;&>三、&/span>&/strong>&span style=&font-family: 微软雅黑, &Microsoft YaHei&;&>由于网络原因、下载知识欠缺、本地电脑&span style=&font-family: 微软雅黑, &Microsoft YaHei&;&>或&/span>手机阻止下载等问题无法解决时,需要提供以下&/span>&span style=&font-family: 微软雅黑, &Microsoft YaHei&; color: rgb(255, 0, 0);&>任意一条信息&/span>&span style=&font-family: 微软雅黑, &Microsoft YaHei&;&>给我们,我们才能更及时地为你服务:&/span>&br/>&/p>&p>&span style=&font-family: 微软雅黑, &Microsoft YaHei&;&>3.1、如果是注册的会员,请告诉我们你的会员账号;&/span>&/p>&p>&span style=&font-family: 微软雅黑, &Microsoft YaHei&;&>3.2、如果是游客下载的,请告诉我们你下载时填写的手机或者邮箱;&/span>&/p>&p>&span style=&font-family: 微软雅黑, &Microsoft YaHei&;&>3.3、如果是微信或QQ快捷登陆的,请告诉我们你的微信或QQ昵称;&/span>&/p>&p>&span style=&font-family: 微软雅黑, &Microsoft YaHei&;&>3.4、如果这些你仍然无法确定,请告诉我们你的付款单号(我们可以通过单号反过来查询你的账号和下载记录)&/span>&a href=&http://www.jinchutou.com/i-93.html& target=&_blank& style=&text-decoration: color: rgb(255, 192, 0); font-family: 微软雅黑, &Microsoft YaHei&;&>&span style=&color: rgb(255, 192, 0); font-family: 微软雅黑, &Microsoft YaHei&;&>看看什么是单号?&/span>&/a>&span style=&font-family: 微软雅黑, &Microsoft YaHei&;&>;&/span>&/p>&p>&strong>&span style=&font-family: 微软雅黑, &Microsoft YaHei&;&>四、&/span>&/strong>&span style=&font-family: 微软雅黑, &Microsoft YaHei&;&>需要下载哪份文档,请发送文档网址,而不是截图,更不要直接把标题给我们;&/span>&br/>&/p>&p>&strong>&span style=&font-family: 微软雅黑, &Microsoft YaHei&;&>五、&/span>&/strong>&span style=&font-family: 微软雅黑, &Microsoft YaHei&;&>其它下载常见问题详见:&/span>&a href=&http://www.jinchutou.com/info-0-23-1.html& target=&_blank& style=&font-family: 微软雅黑, &Microsoft YaHei&;&>http://www.jinchutou.com/info-0-23-1.html&/a>&br/>&/p>&p>&br/>&/p>" />
?&/span>&span id=&_baidu_bookmark_start_4& style=&display: line-height: 0&>?&/span>&p>&span style=&font-family: 微软雅黑, Arial, &Times New Roman&; font-size: 14 background-color: rgb(255, 255, 255);&>& & 鉴于本网发布稿件来源广泛、数量较多, 系统审核过程只针对存在明显违法有害内容(如色情、暴力、反动、危害社会治安及公共安全等公安部门明文规定的违法内容)进行处理,难以逐一核准作者身份及核验所发布的内容是否存在侵权事宜, 如果著作权人发现本网已转载或摘编了其拥有著作权的作品或对稿酬有疑议, 请及时与本网联系删除。&/span>&/p>&p>&strong style=&color: rgb(102, 102, 102); font-family: 微软雅黑, Arial, &Times New Roman&; font-size: 14 white-space: background-color: rgb(255, 255, 255);&>& & 侵权处理办法参考版权提示一文:&/strong>&a href=&http://www.jinchutou.com/h-59.html& target=&_blank& textvalue=&http://www.jinchutou.com/h-59.html&>http://www.jinchutou.com/h-59.html&/a>&span style=&color: rgb(102, 102, 102); font-family: 微软雅黑, Arial, &Times New Roman&; font-size: 14 background-color: rgb(255, 255, 255);&>&&/span>&/p>&p>&span style=&color: rgb(102, 102, 102); font-family: 微软雅黑, Arial, &Times New Roman&; font-size: 14 background-color: rgb(255, 255, 255);&>1、如涉及内容过多,需要发送邮箱,请电子邮箱到,我们会及时处理;&/span>&/p>&p>&span style=&color: rgb(102, 102, 102); font-family: 微软雅黑, Arial, &Times New Roman&; font-size: 14 background-color: rgb(255, 255, 255);&>2、系统一旦删除后,文档肯定是不能下载了的,但展示页面缓存需要一段时间才能清空,请耐心等待2-6小时;&/span>&/p>&p>&span style=&color: rgb(102, 102, 102); font-family: 微软雅黑, Arial, &Times New Roman&; font-size: 14 background-color: rgb(255, 255, 255);&>3、请版权所有人(单位)提供最起码的证明(证明版权所有人),以便我们尽快查处上传人;&/span>&/p>&p>&span style=&color: rgb(102, 102, 102); font-family: 微软雅黑, Arial, &Times New Roman&; font-size: 14 background-color: rgb(255, 255, 255);&>4、请文明对话,友好处理;&/span>&/p>&p>&span style=&color: rgb(102, 102, 102); font-family: 微软雅黑, Arial, &Times New Roman&; font-size: 14 background-color: rgb(255, 255, 255);&>5、为了杜绝以前再有类似的侵权事情,可以为我们提供相应的关键字,便于管理人员添加到系统后能有效排除和抵制与您(贵单位)相关版权作品上传;&/span>&/p>&span id=&_baidu_bookmark_end_5& style=&display: line-height: 0&>?&/span>&span id=&_baidu_bookmark_end_3& style=&display: line-height: 0&>?&/span>" />
&span style=&color: rgb(85, 85, 85); font-family: 微软雅黑; background-color: rgb(255, 255, 255);&>& & 为了维护合法,安定的网络环境,本着开放包容的心态共建共享金锄头文库平台,请各位上传人本着自律和责任心共享发布有价值的文档;本站客服对于上传人服务前,有以下几点可提前参阅:&/span>&/p>&p>&span style=&color: rgb(85, 85, 85); font-family: 微软雅黑; background-color: rgb(255, 255, 255);&>1、本站上传会员收益见:&a href=&http://www.jinchutou.com/h-36.html& target=&_blank&>http://www.jinchutou.com/h-36.html&/a> &/span>&/p>&p>2、本站不会为任何刚注册的上传会员特批解除上传限制,普通会员每天可以上传50份,值班经值会审核其上传内容,请自行观察自己上传的文档哪些在“临时转换中”(审核通过),哪些在审核拒绝中,连续坚持几天都没有任何文档被拒的情况下,根据文档质量和发布分类是否正常等考量合格后值班经理会特批升级会员等级,相应的权益也同时上升。&/p>&p>3、上传人本着友好、合作、共建、共享的原则,请耐心仔细的查看《&a href=&http://www.jinchutou.com/i-143.html& target=&_blank&>违禁作品内容处理规则》;&/a>&a href=&http://www.jinchutou.com/i-143.html& target=&_blank&>http://www.jinchutou.com/i-143.html&/a>&/p>&p>4、上传人可以观注本站公告,查看其它被公示永久封禁的原因&a href=&http://www.jinchutou.com/news-1.html& target=&_blank&>http://www.jinchutou.com/news-1.html&/a>&/p>&p>5、其它问题可以参阅上传常见问题指引:&a href=&http://www.jinchutou.com/info-0-25-1.html& target=&_blank&>http://www.jinchutou.com/info-0-25-1.html&/a>&/p>" />编译原理-实验3算符优先分析法_百度文库
两大类热门资源免费畅读
续费一年阅读会员,立省24元!
编译原理-实验3算符优先分析法
阅读已结束,下载本文需要
想免费下载本文?
定制HR最喜欢的简历
下载文档到电脑,同时保存到云知识,更方便管理
加入VIP
还剩11页未读,
定制HR最喜欢的简历
你可能喜欢好了,现在来尝试把那些该未实现的注释给实现掉了。当然有两个地方在上一节中本应该实现,它们是代码块1中的
// 将一个左括号压入栈顶
opana-&opStack-&push(opana-&opStack,
newOperator(LPARENT, 0x7fffffff, nullOperate));
和代码块3中的
self-&opStack-&push(self-&opStack,
newOperator(token-&type,
/* 未实现:对应的优先级数 */ -1,
unaryOperate));
// 后来加入了优先数表,所以这里可以实现为
self-&opStack-&push(self-&opStack,
newOperator(token-&type,
PRIORITY[token-&type],
unaryOperate));
// 将 MINUS 或 PLUS 作为一元运算符入栈
self-&opStack-&push(self-&opStack,
newOperator(token-&type, 0, unaryOperate));
// 正括号入栈
self-&opStack-&push(self-&opStack, newOperator(token-&type,
PRIORITY[token-&type],
nullOperate));
接下来,先为测试OperationAnalyser做准备,再将那些功能逐个实现。看起来这似乎不容易,不同的语法分析器之间耦合度较紧。不过这个问题可以这样解决:
struct FakeDefaultAnalyser {
memberSyntaxAnalyser
struct FakeVariableAnalyser {
memberSyntaxAnalyser
struct SyntaxAnalyser* newFakeDefaultAnalyser(void);
struct SyntaxAnalyser* newVariableAnalyser(void); // fake one.
void FakeDefaultAnalyser_ConsumeNT(void*, struct AbstractSyntaxNode*);
void FakeDefaultAnalyser_ConsumeT(void*, struct Token*);
void FakeVariableAnalyser_ConsumeT(void*, struct Token*);
这些数据结构以及其对应的成员函数并不需要复杂的设计,即可帮助我们完成测试工作。首先,对于FakeVariableAnalyser,因为测试的目的在于检查OperationAnalyser在遇到标识符时会不会跳转到分析变量的分析器中,所以这个数据结构可以很简单地实现——我们可以假定它一旦读入一个标识符,就把这个标识符打包变成一个VariableNode然后返回给OperationAnalyser——而它的consumeNonTerminal函数甚至根本没必要实现;对应地,测试数据也没有必要太复杂,所以与之相关的函数可以这样实现:
struct SyntaxAnalyser* newVariableAnalyser(void)
struct SyntaxAnalyser* varAna = (struct SyntaxAnalyser*)
allocate(sizeof(struct FakeVariableAnalyser));
varAna-&consumeToken = FakeVariableAnalyser_ConsumeT;
varAna-&consumeNonTerminal = NULL;
return varA
void FakeVariableAnalyser_ConsumeT(void* self, struct Token* tkn)
if(IDENT != tkn-&type) {
fprintf(treeout, "incorrect token passed to variable analyser.\n");
fprintf(treeout, "
token image:
%s\n", tkn-&image);
revert(analyserStack-&pop(analyserStack));
struct SyntaxAnalyser* analyser = (struct SyntaxAnalyser*)
(analyserStack-&peek(analyserStack));
analyser-&consumeNonTerminal(analyser, (struct AbstractSyntaxNode*)
newVariableNode(tkn-&image));
而那个FakeDefaultAnalyser,则是常驻分析器栈栈底的默认分析器。考虑到当OperationAnalyser将自己弹出分析器栈并返回时会有两个动作:将得到的算术节点扔给FakeDefaultAnalyser的consumeNonTerminal函数、将最后一个传入的符号扔给FakeDefaultAnalyser的consumeToken函数,所以这两个成员函数都得实现。参考实现如下:
struct SyntaxAnalyser* newFakeDefaultAnalyser(void)
struct SyntaxAnalyser* defAna = (struct SyntaxAnalyser*)
allocate(sizeof(struct FakeDefaultAnalyser));
defAna-&consumeToken = FakeDefaultAnalyser_ConsumeT;
defAna-&consumeNonTerminal = FakeDefaultAnalyser_ConsumeNT;
return defA
void FakeDefaultAnalyser_ConsumeNT(void* self, struct AbstractSyntaxNode* node)
fprintf(treeout, "\noperation returned.\n");
if(NULL == node) {
fprintf(treeout, "NULL returned.\n");
node-&printree(node, 1);
node-&delNode(node);
void FakeDefaultAnalyser_ConsumeT(void* self, struct Token* t)
if(NULL == t-&image) {
printf("Token passed:
%2d / NULL image\n", t-&type);
printf("Token passed:
%2d / %s\n", t-&type, t-&image);
将节点信息输出到由treeout指向的日志文件中。
数据结构有了,现在准备将词法分析模块包含进来以便测试。词法分析需要的接口函数可以这样弄:
struct Stack* analyserS // 分析器栈指针
char buffer[64];
struct Token token = {
0, SKIP, NULL, buffer
}; // 存放词法分析得到的符号
// 下面变量将用来提供测试数据,在nextChar中会用到
char* testcase[];
int testsuit_nr = 0;
int ch_index = 0;
int nextChar(void)
int ch = (int)testcase[testsuit_nr][ch_index++];
if(0 == ch) {
ch_index = 0;
++testsuit_
struct Token* firstToken(void)
printf("Test case %2d\n", testsuit_nr);
analyserStack-&push(analyserStack, newOperationAnalyser());
// 新建一个OperationAnalyser压到栈顶准备测试
struct Token* nextToken(void)
if(SKIP != token.type) {
struct SyntaxAnalyser* analyser = (struct SyntaxAnalyser*)
(analyserStack-&peek(analyserStack));
analyser-&consumeToken(analyser, &token);
void eofToken(void)
nextToken();
struct SyntaxAnalyser* analyser = (struct SyntaxAnalyser*)
(analyserStack-&peek(analyserStack));
struct Token endToken = {0, END, NULL, NULL};
analyser-&consumeToken(analyser, &endToken);
void error(int line, ErrMsg err)
fprintf(stderr, "Line %d Error: %s\n", line, err);
最后弄个测试驱动器,一个main函数。你可以把这一大串都放到一个.c文件中,独立编译执行。
#include&stdio.h&
/* 引入COOL的MAD来调试内存泄漏 */
/* 请将COOL整个目录放在JerryCompiler所在的目录下 */
#ifndef _DEBUG_MODE
#define _DEBUG_MODE
#endif /* _DEBUG_MODE */
#include"COOL/MemoryAllocationDebugger.h"
#include"datastruct.h"
#include"syntax-node.h"
#include"syntax-analyser.h"
#include"dfa.h"
#include"dfa.c"
FILE* // 用以输出节点信息日志
// 变量和函数声明放这里,以免main中报错
int main()
treeout = fopen("testout", "w"); // 这个文件随便你用什么名字
analyserStack = newStack();
// 分析器栈底常驻一个默认分析器,专门用来输出结果
analyserStack-&push(analyserStack, newFakeDefaultAnalyser());
// testcase数组最后一项应该手动置为NULL
while(NULL != testcase[testsuit_nr]) {
tokenize();
/* 如果最后的内存泄漏查看发现有没被释放的堆空间,那么取消这一块注释,以便查看每一轮测试中的未释放空间 */
printf("Test done.\n");
revert(analyserStack-&pop(analyserStack));
analyserStack-&finalize(analyserStack);
showA // 查看内存泄漏
fclose(treeout);
测试数据参考:
char* testcase[] = {
"1+2 + 5 /6;", // 普通运算
"(1 + 2*(2.4+5))", // 括号
"-(0 == ((!5) & (9)) && 14 &= 100 - -3)", // 各种逻辑运算、负号
"!4 == 7 || 4 == 8 && 4 &= 0 || 1 != 2",
"i * s + d /", // 变量跳转
"a = b = c", // 赋值运算的右结合
";", // 空语句
"1+!s)", // 多余的右括号
"1+k!)", // 不正确的表达式
"(1+ m* 5", // 多余的左括号
在最后实现OperationAnalyser模块之前,为了避免整个项目中“名称用完”的情况发生,这里把一些成员函数的名字作特定包装,以免与其它函数不慎重名而导致莫名其妙的编译错误:
// 各个函数的名字用wrapname这个宏来处理一次
#define wrapname(name) name ## _OperationAnalyser
// 原 consumeToken_OpAna
static void wrapname(consumeToken)(void*, struct Token*);
// 原 consumeNonTerminal_OpAna
static void wrapname(consumeNonTerminal)(void*, struct AbstractSyntaxNode*);
// 原 consumeFactor
static void wrapname(consumeFactor)(struct OperationAnalyser*, struct Token*);
// 原 consumeOperator
static void wrapname(consumeOperator)(struct OperationAnalyser*, struct Token*);
此外还引入一个函数,它将在当前OperationAnalyser完成任务(无论成功或失败)时将它弹出分析器栈,释放它所占据的全部空间:
static void wrapname(cleanup)(struct OperationAnalyser* self)
while(0 != self-&opStack-&getSize(self-&opStack)) {
revert(self-&opStack-&pop(self-&opStack));
self-&opStack-&finalize(self-&opStack);
struct AbstractValueNode*
while(0 != self-&numStack-&getSize(self-&numStack)) {
node = (struct AbstractValueNode*)(self-&numStack-&pop(self-&numStack));
node-&delNode(node);
self-&numStack-&finalize(self-&numStack);
analyserStack-&pop(analyserStack);
revert(self);
现在开始各个击破。先来弄遇到标识符跳转的那块分支,将一个VariableAnalyser加入栈顶并让它去分析:
if(IDENT == token-&type) {
struct SyntaxAnalyser* analyser = newVariableAnalyser();
analyserStack-&push(analyserStack, analyser);
analyser-&consumeToken(analyser, token);
紧接着是这一块代码中最后那个分支。其实这里也不一定是错误,比如刚才构造的测试数据中,如果直接读入分号,或者由于某种原因,OperationAnalyser得到的是一个空的运算,就不是一个错误。不过,假如得到的是空运算的话,那么当前OperationAnalyser的数栈和符号栈应该都处于刚刚初始化的状态,因此
struct AbstractSyntaxNode* ret = (struct AbstractSyntaxNode*)
(self-&numStack-&pop(self-&numStack));
if(NULL != ret || 1 != self-&opStack-&getSize(self-&opStack)) {
// 栈内容已经改变,出错
printf("ERR #0\n"); // TODO
wrapname(cleanup)(self);
struct SyntaxAnalyser* analyser = (struct SyntaxAnalyser*)
(analyserStack-&peek(analyserStack));
analyser-&consumeNonTerminal(analyser, ret);
analyser = (struct SyntaxAnalyser*)
(analyserStack-&peek(analyserStack));
analyser-&consumeToken(analyser, token);
这里错误处理并没有完结,语法分析的错误处理方式将放到以后去弄。
最后两个是wrapname(consumeOperator)中最后两个分支中未实现的部分。其中当前符号为反括号的那一分支(条件为RPARENT == token-&type)中,当反括号多1导致常驻栈底的那个正括号被配对的情况中(条件为0 == self-&opStack-&getSize(self-&opStack),右方还标出“注2”的那句),其实并不需要报错,而完全可以把这个多出来的反括号扔给下一个语法分析器去做:
if(0 == self-&opStack-&getSize(self-&opStack)) {
struct AbstractSyntaxNode* ret = (struct AbstractSyntaxNode*)
(self-&numStack-&pop(self-&numStack));
wrapname(cleanup)(self);
struct SyntaxAnalyser* analyser = (struct SyntaxAnalyser*)
(analyserStack-&peek(analyserStack));
analyser-&consumeNonTerminal(analyser, ret);
analyser = (struct SyntaxAnalyser*)
(analyserStack-&peek(analyserStack));
analyser-&consumeToken(analyser, token); // 多的反括号传递过去~
而最后注释有终止的那一句,其实跟这个也差不多,先把代码贴出来:
struct AbstractSyntaxNode*
struct Operator* topOp = (struct Operator*)
(self-&opStack-&pop(self-&opStack));
while(LPARENT != topOp-&op) {
topOp-&operate(topOp, self-&numStack);
topOp = (struct Operator*)(self-&opStack-&pop(self-&opStack));
topOp-&operate(topOp, NULL);
ret = (struct AbstractSyntaxNode*)(self-&numStack-&pop(self-&numStack));
if(0 != self-&opStack-&getSize(self-&opStack)) {
printf("ERR #1\n"); // TODO
wrapname(cleanup)(self);
struct SyntaxAnalyser* analyser = (struct SyntaxAnalyser*)
(analyserStack-&peek(analyserStack));
analyser-&consumeNonTerminal(analyser, ret);
analyser = (struct SyntaxAnalyser*)
(analyserStack-&peek(analyserStack));
analyser-&consumeToken(analyser, token);
前面直到while循环是不断弹出操作符栈栈顶进行运算;后面那句
topOp-&operate(topOp, NULL);
看起来很莫名其妙?想想这时topOp是什么。它必然是一个左括号,因此它进行运算仅仅是释放它自己的内存空间(不直接写成释放空间的原因是,考虑到代码的封装性,我们应该假设自己不知道这些Operator是怎么来的,只知道它们应该都被运算掉);另外,加入输入是正确的,那么这个左括号应该是常驻栈顶的正括号,而它是被pop出来的,这也就意味着这样一来符号栈就应该是空的了,如果这是符号栈里面还有东西(if(0 != self-&opStack-&getSize(self-&opStack))),那就意味着左括号数量多了,因此出错。
现在就可以开始测试了。测试的结果有一些会从标准输出打印出来,如那些“ERR #”之类的信息,而语法树信息则会输出到treeout指向的文件中。Enjoy~
语法树信息的格式
输出到文件里面的信息看起来不是那么友好。也许注意一下它们的缩进会有所帮助,如
1 + 2 + 5 / 6
对应的输出为
operation returned.
binary operation - operator : + ~~ left operand:
binary operation - operator : + ~~ left operand:
right operand:
right operand:
binary operation - operator : / ~~ left operand:
right operand:
第一行“operation returned.”是在FakeDefaultAnalyser_ConsumeNT中输出的,而后面的那一串都是在printBinaryOperation中输出的。注意到第2行和第6行缩进相同,这表示它们是一个节点的两个部分。第2行之后缩进增加的那些部分是该BinaryOperationNode节点的leftOperand所指向的子树,而第6行之后那些部分则是其rightOperand指向的子树。所以这表示出来的是这样一棵树:
&---这个斜杠是除号
此外还有UnaryOperationNode,它的形式如
unary operation - operator : %运算符% ~~ operand:
运算数节点信息
indent printBinaryOperationNode printUnaryOperationNode 函数,syntax-node.c 文件
附调试信息输出被注释掉的完整的代码:
/* syntax-analyser.h */
#ifndef _SYNTAX_ANALYSER_H
#define _SYNTAX_ANALYSER_H
#include"datastruct.h"
struct OperationAnalyser* newOperationAnalyser(void);
/* 暂时将.c文件用.h文件来包含,而不是用makefile */
#include"operation-analyser.c"
#endif /* _SYNTAX_ANALYSER_H */
/* operation-analyser.c */
#include&stdlib.h&
#include"datastruct.h"
#include"syntax-analyser.h"
#include"syntax-node.h"
#ifndef _DEBUG_MODE
#define _DEBUG_MODE
#endif /* _DEBUG_MODE */
#include"COOL/MemoryAllocationDebugger.h"
#include"COOL/Collection/Stack/Stack.h"
extern struct Stack* analyserS
extern struct SyntaxAnalyser* newVariableAnalyser(void);
struct Operator {
void (*operate)(struct Operator*, struct Stack*);
int rightC
static const int RIGHT_COMB[] = {
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0,
static struct Operator* newOperator(AcceptType op, int priority,
void (*operate)(struct Operator*, struct Stack*));
static void nullOperate(struct Operator*, struct Stack*);
static void unaryOperate(struct Operator*, struct Stack*);
static void binaryOperate(struct Operator*, struct Stack*);
static struct Operator* newOperator(AcceptType op, int priority,
void (*operate)(struct Operator*, struct Stack*))
struct Operator* oper = (struct Operator*)allocate(sizeof(struct Operator));
oper-&op =
oper-&priority =
oper-&rightCombination = RIGHT_COMB[op];
oper-&operate =
static void nullOperate(struct Operator* oper, struct Stack* numStack)
revert(oper);
static void unaryOperate(struct Operator* oper, struct Stack* numStack)
struct AbstractValueNode*
value = (struct AbstractValueNode*)(numStack-&pop(numStack));
numStack-&push(numStack, newUnaryOperationNode(oper-&op, value));
revert(oper);
static void binaryOperate(struct Operator* oper, struct Stack* numStack)
struct AbstractValueNode* left,*
right = (struct AbstractValueNode*)(numStack-&pop(numStack));
left = (struct AbstractValueNode*)(numStack-&pop(numStack));
numStack-&push(numStack, newBinaryOperationNode(oper-&op, left, right));
revert(oper);
#define wrapname(name) name ## _OperationAnalyser
static void wrapname(consumeToken)(void*, struct Token*);
static void wrapname(consumeNonTerminal)(void*, struct AbstractSyntaxNode*);
static void wrapname(consumeFactor)(struct OperationAnalyser*, struct Token*);
static void wrapname(consumeOperator)(struct OperationAnalyser*, struct Token*);
static void wrapname(cleanup)(struct OperationAnalyser*);
static const int PRIORITY[] = {
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0,
2, 2, 1, 1, 5, 3, 3, 3, 3, 3, 3,
0, 0, 0x7fffffff, 0, 0, 0, 0, 0
static void (*OPER_FUNCS[])(struct Operator*, struct Stack*) = {
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL,
binaryOperate, binaryOperate, binaryOperate, binaryOperate, binaryOperate,
binaryOperate, binaryOperate, binaryOperate,
binaryOperate, binaryOperate, binaryOperate,
binaryOperate, binaryOperate, unaryOperate
struct OperationAnalyser* newOperationAnalyser(void)
struct OperationAnalyser* opana = (struct OperationAnalyser*)
allocate(sizeof(struct OperationAnalyser));
opana-&needFactor = 1;
opana-&numStack = newStack();
opana-&opStack = newStack();
opana-&opStack-&push(opana-&opStack,
newOperator(LPARENT, 0x7fffffff, nullOperate));
opana-&consumeToken = wrapname(consumeToken);
opana-&consumeNonTerminal = wrapname(consumeNonTerminal);
static void wrapname(consumeToken)(void* self, struct Token* token)
struct OperationAnalyser* opana = (struct OperationAnalyser*)
if(opana-&needFactor) {
printf("... Passing Factor ... %s\n", token-&image);
wrapname(consumeFactor)(opana, token);
printf("... Passing Operator ... %s\n", token-&image);
wrapname(consumeOperator)(opana, token);
static void wrapname(consumeFactor)(struct OperationAnalyser* self,
struct Token* token)
if(NOT == token-&type) {
self-&opStack-&push(self-&opStack,
newOperator(token-&type,
PRIORITY[token-&type],
unaryOperate));
self-&needFactor = 1;
} else if(MINUS == token-&type || PLUS == token-&type) {
self-&opStack-&push(self-&opStack,
newOperator(token-&type, 0, unaryOperate));
self-&needFactor = 1;
} else if(IDENT == token-&type) {
struct SyntaxAnalyser* analyser = newVariableAnalyser();
analyserStack-&push(analyserStack, analyser);
analyser-&consumeToken(analyser, token);
} else if(INTEGER == token-&type) {
self-&numStack-&push(self-&numStack,
newIntegerNode(atoi(token-&image)));
self-&needFactor = 0;
} else if(REAL == token-&type) {
self-&numStack-&push(self-&numStack, newRealNode(atof(token-&image)));
self-&needFactor = 0;
} else if(LPARENT == token-&type) {
self-&opStack-&push(self-&opStack, newOperator(token-&type,
0x7fffffff,
nullOperate));
self-&needFactor = 1;
struct AbstractSyntaxNode* ret = (struct AbstractSyntaxNode*)
(self-&numStack-&pop(self-&numStack));
if(NULL != ret || 1 != self-&opStack-&getSize(self-&opStack)) {
printf("ERR #0\n");
wrapname(cleanup)(self);
struct SyntaxAnalyser* analyser = (struct SyntaxAnalyser*)
(analyserStack-&peek(analyserStack));
analyser-&consumeNonTerminal(analyser, ret);
analyser = (struct SyntaxAnalyser*)
(analyserStack-&peek(analyserStack));
analyser-&consumeToken(analyser, token);
static void wrapname(consumeOperator)(struct OperationAnalyser* self,
struct Token* token)
int priority = PRIORITY[token-&type];
if(0 & priority && priority & PRIORITY[LPARENT]) {
int push = 0;
struct Operator* topOp = (struct Operator*)
(self-&opStack-&peek(self-&opStack));
push |= (priority & topOp-&priority);
push |= (priority == topOp-&priority && topOp-&rightCombination);
while(!push) {
printf("Operating ... %s\n", OPERATORS[topOp-&op]);
topOp = (struct Operator*)(self-&opStack-&pop(self-&opStack));
topOp-&operate(topOp, self-&numStack);
topOp = (struct Operator*)(self-&opStack-&peek(self-&opStack));
push |= (priority & topOp-&priority);
push |= (priority == topOp-&priority && topOp-&rightCombination);
self-&opStack-&push(self-&opStack, newOperator(token-&type,
OPER_FUNCS[token-&type]));
self-&needFactor = 1;
} else if(RPARENT == token-&type) {
struct Operator* topOp = (struct Operator*)
(self-&opStack-&pop(self-&opStack));
while(nullOperate != topOp-&operate) {
printf("Operating ... %s\n", OPERATORS[topOp-&op]);
topOp-&operate(topOp, self-&numStack);
topOp = (struct Operator*)(self-&opStack-&pop(self-&opStack));
topOp-&operate(topOp, self-&numStack);
self-&needFactor = 0;
if(0 == self-&opStack-&getSize(self-&opStack)) {
struct AbstractSyntaxNode* ret = (struct AbstractSyntaxNode*)
(self-&numStack-&pop(self-&numStack));
wrapname(cleanup)(self);
struct SyntaxAnalyser* analyser = (struct SyntaxAnalyser*)
(analyserStack-&peek(analyserStack));
analyser-&consumeNonTerminal(analyser, ret);
analyser = (struct SyntaxAnalyser*)
(analyserStack-&peek(analyserStack));
analyser-&consumeToken(analyser, token);
struct AbstractSyntaxNode*
struct Operator* topOp = (struct Operator*)
(self-&opStack-&pop(self-&opStack));
while(LPARENT != topOp-&op) {
topOp-&operate(topOp, self-&numStack);
topOp = (struct Operator*)(self-&opStack-&pop(self-&opStack));
topOp-&operate(topOp, NULL);
ret = (struct AbstractSyntaxNode*)(self-&numStack-&pop(self-&numStack));
if(0 != self-&opStack-&getSize(self-&opStack)) {
printf("ERR #1\n");
wrapname(cleanup)(self);
struct SyntaxAnalyser* analyser = (struct SyntaxAnalyser*)
(analyserStack-&peek(analyserStack));
analyser-&consumeNonTerminal(analyser, ret);
analyser = (struct SyntaxAnalyser*)
(analyserStack-&peek(analyserStack));
analyser-&consumeToken(analyser, token);
static void wrapname(consumeNonTerminal)(void* self,
struct AbstractSyntaxNode* node)
struct OperationAnalyser* opana = (struct OperationAnalyser*)
opana-&numStack-&push(opana-&numStack, node);
opana-&needFactor = 0;
static void wrapname(cleanup)(struct OperationAnalyser* self)
while(0 != self-&opStack-&getSize(self-&opStack)) {
revert(self-&opStack-&pop(self-&opStack));
self-&opStack-&finalize(self-&opStack);
struct AbstractValueNode*
while(0 != self-&numStack-&getSize(self-&numStack)) {
node = (struct AbstractValueNode*)(self-&numStack-&pop(self-&numStack));
node-&delNode(node);
self-&numStack-&finalize(self-&numStack);
analyserStack-&pop(analyserStack);
revert(self);
#undef wrapname
/* test-op-ana.c */
#include&stdio.h&
#ifndef _DEBUG_MODE
#define _DEBUG_MODE
#endif /* _DEBUG_MODE */
#include"COOL/MemoryAllocationDebugger.h"
#include"datastruct.h"
#include"syntax-node.h"
#include"syntax-analyser.h"
#include"dfa.h"
#include"dfa.c"
struct FakeDefaultAnalyser {
memberSyntaxAnalyser
struct FakeVariableAnalyser {
memberSyntaxAnalyser
struct SyntaxAnalyser* newFakeDefaultAnalyser(void);
struct SyntaxAnalyser* newVariableAnalyser(void); // fake one.
void FakeDefaultAnalyser_ConsumeNT(void*, struct AbstractSyntaxNode*);
void FakeDefaultAnalyser_ConsumeT(void*, struct Token*);
void FakeVariableAnalyser_ConsumeT(void*, struct Token*);
struct Stack* analyserS
int testsuit_nr = 0;
int ch_index = 0;
char buffer[64];
struct Token token = {
0, SKIP, NULL, buffer
char* testcase[] = {
"1+2 + 5 /6;", // 普通运算
"(1 + 2*(2.4+5))", // 括号
"-(0 == ((!5) & (9)) && 14 &= 100 - -3)", // 各种逻辑运算、负号
"!4 == 7 || 4 == 8 && 4 &= 0 || 1 != 2",
"i * s + d /", // 变量跳转
"a = b = c", // 赋值运算的右结合
";", // 空语句
"1+!s)", // 多余的右括号
"1+k!)", // 不正确的表达式
"(1+ m* 5", // 多余的左括号
int main()
treeout = fopen("testout", "w");
analyserStack = newStack();
analyserStack-&push(analyserStack, newFakeDefaultAnalyser());
while(NULL != testcase[testsuit_nr]) {
tokenize();
printf("Test done.\n");
revert(analyserStack-&pop(analyserStack));
analyserStack-&finalize(analyserStack);
fclose(treeout);
struct SyntaxAnalyser* newFakeDefaultAnalyser(void)
struct SyntaxAnalyser* defAna = (struct SyntaxAnalyser*)
allocate(sizeof(struct FakeDefaultAnalyser));
defAna-&consumeToken = FakeDefaultAnalyser_ConsumeT;
defAna-&consumeNonTerminal = FakeDefaultAnalyser_ConsumeNT;
return defA
struct SyntaxAnalyser* newVariableAnalyser(void)
struct SyntaxAnalyser* varAna = (struct SyntaxAnalyser*)
allocate(sizeof(struct FakeVariableAnalyser));
varAna-&consumeToken = FakeVariableAnalyser_ConsumeT;
varAna-&consumeNonTerminal = NULL;
return varA
void FakeDefaultAnalyser_ConsumeNT(void* self, struct AbstractSyntaxNode* node)
fprintf(treeout, "\noperation returned.\n");
if(NULL == node) {
fprintf(treeout, "NULL returned.\n");
node-&printree(node, 1);
node-&delNode(node);
void FakeVariableAnalyser_ConsumeT(void* self, struct Token* tkn)
if(IDENT != tkn-&type) {
fprintf(treeout, "incorrect token passed to variable analyser.\n");
fprintf(treeout, "
token image:
%s\n", tkn-&image);
revert(analyserStack-&pop(analyserStack));
struct SyntaxAnalyser* analyser = (struct SyntaxAnalyser*)
(analyserStack-&peek(analyserStack));
analyser-&consumeNonTerminal(analyser, (struct AbstractSyntaxNode*)
newVariableNode(tkn-&image));
void FakeDefaultAnalyser_ConsumeT(void* self, struct Token* t)
if(NULL == t-&image) {
printf("Token passed:
%2d / NULL image\n", t-&type);
printf("Token passed:
%2d / %s\n", t-&type, t-&image);
int nextChar(void)
int ch = (int)testcase[testsuit_nr][ch_index++];
if(0 == ch) {
ch_index = 0;
++testsuit_
struct Token* firstToken(void)
printf("Test case %2d\n", testsuit_nr);
analyserStack-&push(analyserStack, newOperationAnalyser());
struct Token* nextToken(void)
if(SKIP != token.type) {
struct SyntaxAnalyser* analyser = (struct SyntaxAnalyser*)
(analyserStack-&peek(analyserStack));
analyser-&consumeToken(analyser, &token);
void eofToken(void)
nextToken();
struct SyntaxAnalyser* analyser = (struct SyntaxAnalyser*)
(analyserStack-&peek(analyserStack));
struct Token endToken = {0, END, NULL, NULL};
analyser-&consumeToken(analyser, &endToken);
上次忘了说了,在最开头的两个分析例程中,为什么要把算符分析的设置NEEDFACTOR重置为1的代码放在cosume-non-token里面?我觉得在cusumeFactor那个函数里面的对具体的每个结束分析之后都一律调用设置0/1会比较直观一些^^或许有其中的必要性?consumeNonTerminal这个函数并不是一个“重置”函数,因为只有在遇到变量时(确切的说,是在遇到IDENT时),OperationAnalyser才会调用另一分析器来获取一个变量,因此当这个分析器返回时,调用OperationAnalyser的consumeNonTerminal函数时传入的一定是一个VariableNode,所以此时跟遇到INTEGER或REAL一样,要将needFactor置0后面一句不太明白你想表达什么。
浏览: 39021 次
来自: 武汉
之前自搭 blog, 太累. 最近在研究 C艹 语言的错误处理 ...
话说很久没有看到你更新BLOG了,最近在研究LINUX开发^^ ...
RednaxelaFX 写道我的见识还是太少了……最近在某在x ...
我的见识还是太少了……最近在某在x86上跑的东西的手写汇编里的 ...
lwwin 写道没想到一个NOP把FX也拉来了^^关于代码…… ...
(window.slotbydup=window.slotbydup || []).push({
id: '4773203',
container: s,
size: '200,200',
display: 'inlay-fix'

我要回帖

更多关于 编译原理 文法分析 的文章

 

随机推荐