联系方式    |    在线留言 您好,pp电子欢迎访问这里是您的网站名称官网!
PP电子官方网站
客服热线400-123-4567
公司新闻

pp电子Lex与YACC详解

作者:小编    发布时间:2024-03-21 11:24:28    浏览量:

  只消你正在Unix处境中写流圭表,你肯定会重逢奥妙的Lex&YACC,就如GNU/Linux用户所熟知的Flex&Bison,这里的Flex即是由Vern Paxon实行的一个Lex,Bison则是GNU版本的YACC。正在此咱们将联合称号这些圭表为Lex和YACC。新版本的圭表是向上兼容的(译注:即兼容老版本),于是你可能用Flex和Bison来测验下咱们的实例。这些圭表适用性极广,但似乎你的C编译器一律,正在其主页上并没有描摹它们,也没相合于怎么利用的消息。当和Lex联结利用时pp电子,YACC实正在是棒极了,然则Bison的主页上并没有描摹Bison若何跟Lex联结利用以天生代码的相应声明。

  假设利用恰当,这些圭表(指LEX&YACC)可能让你随便的解析庞杂的发言,当你须要读取一个设备文献时,或者你须要编写一个你本人利用的发言的编译器时,这对待你来说是莫大的裨益恒温器。本文档能供应给你少少帮帮,你将展现你再也不必手工写解析器了,Lex & YACC即是为你量身打造的利器。

  Lex会天生一个叫做『词法判辨器』的圭表。这是一个函数,它带有一个字符传播入参数,词法判辨器函数看到一组字符就会去成亲一个合节字(key),采纳相应法子。一个很是大略的例子(example1)如下:

  第一局部,位于%{和%}对之间直接包括了输出圭表(stdio.h)。咱们须要这个圭表,由于利用了printf函数,它正在stdio.h中界说恒温器。第二局部用’%%’盘据开来,于是第二行开始于’stop’,一朝正在输入参数中遭遇了’stop’,接下来的那一行(printf()挪用)将被实施。除此除表,尚有’start’,其跟stop的行径差不多。咱们再次用’%%’闭幕代码段。为了编译上面的例子,只须要实施以下敕令:

  戒备:假设你用flex,则就将lex敕令用flex代庖,还须要将’-ll’选项改成’-lfl’。正在RedHat 6.x以及SuSe中须要云云做。云云,Lex将天生’example1’这个文献。运转该文献,它将守候你输入少少数据。每次你输入少少不可亲的敕令(非’stop’和’start’),它会将你输入的字符再次输出。你若输入’stop’,它将输出’Stop command received’。用一个EOF(^D)来闭幕圭表。也许你念知晓,它是怎样运转的,由于咱们并没有界说main()函数。这个函数(指main())依然正在lib1(liblex)中界说好了,正在此咱们选用了编译选项’-ll’

  惯破例达式是一种利用元发言的形式描摹。表达式由符号构成。符号凡是是字符和数字,尚有少少拥有迥殊寄义的其他标志,如下表:

  这个实例(example2)自己并没什么用途,下一个实例也不会提及正则表达式。但这里它涌现了若何正在Lex中利用正则表达式,这正在后面将很是有效。

  该Lex文献描摹了两种token成亲:WORDs和NUMBERs。正则表达式很是恐惧,然则只须要稍花力气便可能加以领会。个中NUMBER成亲“[0123456789]+”可能写成“[0-9]+”。WORD成亲就有点庞杂:[a-zA-Z][a-zA-Z0-9]*第一局部仅仅成亲一个’a’到’z’或’A’到’Z’之间的字符,也即一个字母。接着该字母后面须要连上0个或多个字符,这些字符可能是字母,也可能是数字。这里为何用’*’? ’+’暗示起码1次的成亲。一个WORD只要一个字符也可能很好的成亲,正在第一局部咱们依然成亲到了一个字符,于是第二局部可能是0个成亲,于是用’*’。用这种方法,咱们就师法了良多编程发言中对待一个变量名的请求,即请求变量名『务必』以字母着手,然则可能正在后续字符顶用数字。也即是说’temperature1’是一个准确的定名,然则’1temperature’就不是。像example1一律编译example2,并输入少少文本,如下:

  你也许会嫌疑,全体的输出中的空格是从哪来的?因由很大略:从输入而来,咱们不正在空格上成亲任何实质,于是它们又输出来了。Flex主页上有正则表达式的具体文档。良多人感应perl正则表达式主页的声明很是有效,然则Flex并不实行perl所实行的全体东西。你只须要确保不写少少形如’[0-9]*’的空成亲即可,你的词法判辨器(由Flex天生)将不明就里的着手接续的成亲空字符。

  YACC可能解析输入流中的标识符(token),这就了了的描摹了YACC和LEX的干系,YACC并不知晓『输入流』为何物,它须要事先就将输入流预加工成标识符,固然你可能本人手工写一个Tokenizer,但咱们将这些职责留给LEX来做。YACC用来为编译器解析输入数据,即圭表代码。这些用编程发言写成的圭表代码一点也不优柔寡断——它们只要一个意义。正由于这样,YACC才不会去周旋那些有歧义的语法,而且会埋怨shift/reduce或者reduce/reduce冲突。更多的合于朦胧性和YACC『题目』可能正在『冲突』一章中找到。

  假定咱们有一个温度计,咱们要用一种大略的发言来掌管它。合于此的一个会话、如下:

  有两个中心须要戒备:第一,咱们包括了『y.tab.h』;第二,咱们不再打印输出了,咱们返回标识符的名字。之所云云做是由于咱们将这些返回传送给了YACC,而它对待咱们屏幕上的输出并不伤风。 『y.tab.h』中界说了这些标识符。然则y.tab.h从哪里来?它由YACC从咱们编写的语法文献中天生,语法文献很是大略,如下:

  第一局部,咱们称之为根(root)。它告诉咱们有一个『commands』,而且这些『commands』由单个的『command』构成。正如你所见到的那样,这是一个尺度的递归布局,由于它又再次包括了『commands』。这意味着该圭表可能一个个的递减一系列的敕令。参见『LEX和YACC内部职责道理』一章,阅读更多的递归细节。第二个礼貌界说了『command』的实质。咱们只假定两种敕令。一个heat_switch由HEAT标识符构成pp电子,它后面随着一个状况,该状况正在LEX中界说,为『on』或『off』。target_set稍微有点庞杂,它由TARGET标识符、TEMPERATURE以及一个数字构成。前面的谁人例子只要YACC文献的语法局部,开始正在YACC文献中尚有其它实质,无缺的YACC文献如下:

  函数yyerror()正在YACC展现一个舛误的时刻被挪用pp电子,咱们只是大略的输出舛误消息,但本来还可能做少少更美丽的事故,参见文档尾的『进阶阅读』局部。yywrap()函数用于接续的从一个文献中读取数据,当遭遇EOF时,你可能再输入一个文献,然后返回0,你也可能使得其返回1,暗意着输入闭幕。更多细节,参见『YACC和LEX内部若何职责的?』一章。接着,这里有一个main()函数,它根基什么也不做,只是挪用少少函数。结果一行大略的界说了我将利用的标识符,假设挪用YACC时,利用『-d』选项,那么它们会输出到y.tab.h中。编译并运转恒温掌管器:

  正在此,状况有所改换,咱们现正在挪用YACC来编译咱们的圭表,它创筑了y.tab.c和y.tab.h. 我接着再挪用LEX。 编译时,咱们去除了『-ll』编译选项,由于此时咱们有了本人的main()函数,并不须要libl来供应。戒备:假设正在编译流程中报错说找不到『yylval』,那么正在example4.l的#include y.tab.h下面加上:

  咱们依然可能准确的解析温度计敕令了,而且能对少少舛误做标志。但也许少少嚚猾的人会猜忌说,该解析器并不知晓你应当做什么,也没有经管少少你输入的数值。让咱们来增加能读取新的温度参数的成效。为抵达此主意,咱们得知晓LEX中的NUMBER成亲要转化成一个数值,然后才具为YACC所授与.每当LEX成亲到了一个对象字串,它就将该成亲文本赋值给『yytext』,YACC则次第正在『yylval』中来查找一个值,正在example5中,咱们可能取得一个大白的计划:

  如你所见,咱们正在yytext顶用了atoi(),并将结果存储正在yylval中,使得YACC可能『望见』它。 同理,咱们再经管STATE成亲,将其与『on』对照,若念等,则将yylval创立为1。接下来,咱们就得考查YACC若何来应对。咱们来看看新的temperature target礼貌创立:

  为取得礼貌中第三局部的值(NUMBER),咱们用『$3』来暗示,每次yylex()返回时,yylval的值便倚赖到了终结符上,其值可能通过『$-常数』来获取。为更进一步加深领会,咱们来看『heat_switch』礼貌:

  之前咱们依然将LEX文献写好了,接下来只须要编写YACC语法文献,而且对词法判辨器做少少编削,使得其可能返回少少值给YACC。example6中的词法判辨器如下:

  留神的话,你会展现yylval依然改换了!咱们不再以为它是一个整数,而是假定为一个char*类型数据。为连结大略性,咱们挪用strdup并是以消磨了良多内存。但这并不影响你解析这个文献。咱们须要生存字符串的值,正在此咱们经管的都是少少定名,文献名以及区域命。鄙人一章,咱们将疏解若何周旋少少庞杂类型的数据。为通告YACC合于yylval的新类型,咱们正在YACC的语法文献中增加一行:

  下面是无缺的YACC文献,语法对照庞杂,提倡联结代码画AST树来帮帮领会(原文这里的代码有不少题目,下面是修改后的代码,对语法也简化了一点点):

  固然LEX和YACC的史册要早于C++,然则仍旧可能用它们来天生一个C++解析器。但咱们用LEX来天生C++的词法判辨器,YACC并不知晓若何直接来经管这些,于是咱们不策动这么做。我以为对照好的做法是,要做一个C++解析器,就须要LEX天生一个C文献,而且让YACC来天生C++代码。但当你这么做的时刻,正在这个流程中你将会遭遇题目,由于C++代码默认状况下并不行找到C的函数,除非你将那些函数界说为extern “C”。为达此主意,咱们正在YACC代码中编写一个C着手:

  这是由于C++中的一个合于界说的礼貌,即不答允yydebug的多处界说。你还恐怕展现,你须要正在你的LEX文献中反复#define YYSTYPE,这是因为C++中庄重的类型搜检(机造)形成的。服从如下方法来编译:

  因为-o选项的存正在,y.tab.h现正在酿成example_cpp.hpp,记住这点。总结: 不要自寻不快的正在C++中编译你的词法判辨器,让它呆正在C的领地里。正在C++中编写解析器时,(也得)确保向编译器注释了了,即你的C函数都有一个extern “C”声明。

  正在YACC文献中,你界说了你本人的main()函数,它正在某个点上挪用了yyparse()。YACC会创筑你的yyparse()函数,并正在y.tab.c中闭幕该函数。yyparse()函数读取一个『标识符/值对』(token/value pairs)流,这些流须要事先就供应,这些流可能是你本人手写的代码供应的,也可能是LEX天生的。正在咱们的示例中,咱们把这个职责丢给了LEX。LEX天生的yylex()函数从文献参数FILE *file中读取字符(文献名为yyin)。假设不创立成yyin,则默以为尺度输入,它会输出到yyout中,假设不加创立,即是stdout。你可能正在yywrap()函数中编削位于文献尾的yyin.yywrap()函数。这些编削使得你可能掀开另少少文献,并不断解析。假设是这种状况,那么就让yywrap()返回0,假设你念正在该文献上闭幕解析,就让它返回1。每次yylex()挪用都市返回一个整数值,该值代表了一个标识符类型(token type)。它告诉YACC,依然读取了这种标识符。该标识符可能有一个值,它应当存放正在yylval变量中。yylval的默认类型为int,然则你可能编削其类型,通过正在YACC文献中#define YYSTYPE。词法判辨器须要也许拜望yylval,为抵达此功效,(yylval)务必正在词法判辨器(lexer)中被声明为一个表部变量(extern variable)。原先的YACC渺视了这点,并没有为你干这项职责,于是,你务必增加以下代码到你的词法判辨器中,就正在#include y.tab.h下面:

  正在前面我依然说过,yylex()须要返回它遭遇了一个什么标识符类型,并将其值存储正在yylval中。当这些标识符正在%token敕令中界说时,它们就被授予了少少数字ID,从256着手。因为这个底细,(咱们)可能将全体的ascii字符看成标识符。假定你要写一个盘算器,到现正在为止,咱们恐怕依然云云写了其词法判辨器:

  没有须要弄这么庞杂。用字符行为速记法来行为标识符的数字ID,咱们可能云云来重写咱们的词法判辨器:

  结果一行成亲任何的单个字符,不然即是不可亲字符。而YACC的语法文献则是云云:

  云云特别简短而直观,你就不必正在文献头用%token来界说那些ascii字符了。另一方面,云云构造尚有少少好处,它可能成亲全体丢给它的东西,而避免了将那些不可亲的输入输出到尺度输出的默认行径。假设用户正在如今盘算器上输入一个’^’字符,将会导致一个解析舛误,而不是将其输出到尺度输出中。

  递归是YACC一个极其紧急的性子。没有递归的话,你就确定一个文献是由一系列独立的敕令构成仍旧由语句构成。因为YACC自己的性子,它只对第一个礼貌或谁人你将其安排为『开始礼貌』的礼貌感有趣。开始礼貌用’%start’符号标志。YACC中的递归以两种情势产生,左递归和右递归。左递归是你应当每每利用的,它们看起来如下:

  这是正在说,一个command要么为空,要么它包括了更多的commands,后面再跟一个command。YACC的这种职责方法意味着它现正在可能随便的剔除单个的command群并一步步简化(经管)。拿上面的例子和下面的右递归做对照:

  然则云云做开销有点大,假设(commands)是%start礼貌(即开始礼貌),那么YACC会将全体的commands生存正在你的栈数据中(file on the stack)pp电子Lex与YACC详解,这将消磨豪爽内存。于是,正在解析长的语句时,务必利用左递归,比方全豹文献。但有时难以避免右递归,可是,假设你的语句并不太长,你就没有须要越轨利用左递归。假设你有少少东西来终结(是以而盘据)你的commands,右递归就很是适合了,但开销仍旧有点大:

  本文档的早期版本舛误的利用了右递归。Markus Triska友情的提示咱们这点(舛误)。

  现正在,咱们须要界说yylval的类型。然则这并不不停恰如其当恒温器。咱们恐怕会多次云云做,由于须要经管多种数据类型。回到咱们假定的谁人恒温器,恐怕你念选拔掌管一个加热器,比方:

  云云的话,就请求yylval是一个union,它可能存储字符串,也可能存储整数,但并不是同时存储。回顾之前咱们讲过,咱们提前通告YACC哪种yylval类型会要经管是通过界说YYSTYPE来实行。同样,咱们可能界说YYSTYPE为一个union,YACC中有一种简洁的格式来实行,即%union语句。正在example4根本上恒温器,我编写example7的YACC语法:

  咱们界说了union,它只包括一个整数和一个字符串。接着利用了一个扩展的%token语法,咱们向YACC注释了应当获取union哪个局部的标识符。正在本例中,咱们让STATE标识符用一个整数(来暗示),这跟之前一律。NUMBER同理,咱们之前用来读取温度。然则WORD有所改换,它声明为须要一个字符串。词法解析器文献有所改换:

  正如你所见,咱们不再直接拜望yylval,咱们增加了一个后缀来声明咱们要拜望谁人局部。咱们不再须要正在YACC语法文献中来干这个职责,YACC正在这里耍了下妖术:

  因为上面的%token声明pp电子,YACC主动选拔了union中的’string’成员。戒备这里$2中存储的一份拷贝pp电子,正在后面它会告诉用户发送死令到哪个heater(须要正在C文献头界说char *heater):

  良多状况下,咱们不盼望从尺度输入解析,而盼望解析给定的字符串。实行格式是自界说实行YY_INPUT,的确做法如下:

  YACC中有很多调试反应消息。这些调试消息的价格有点高,于是你须要供应少少开合来掀开它。当调试你的语法时恒温器,正在YACC敕令行中增加—debug和—verbose选项,正在你的C文献头中增加以下语句:

  这将天生一个y.output文献,个中声通晓所创筑的谁人状况机。当你运转谁人天生的二进造文献,它将输出良多运转时消息。内里包括如今所运转的状况机以及读取到的少少标识符pp电子。Peter Jinks写了一篇合于调试的作品,他正在个中讲述了少少常见得舛误以及若何修改这些舛误。

  YACC解析器正在内部运转的是一个『状况机』,该状况机可能有多种转台。接着有多个礼貌来管造状况间的互相转化。任何实质都是从『root』礼貌着手。正在example7的y.output文献中:

  默认景象下,这个状况机从『commands』礼貌着手递减演化,这也是咱们之前的谁人递归礼貌,它界说了『commands』并从单个的『command』实行构造,后面随着一个分号,然后恐怕再随着更多的『commands』。这个状况机接续递减演化,直到它遭遇某些它能领会的东西,正在本例中,为一个ZONETOK,也即单词『zone』。然后转化到状况1,正在此,进一步经管一个zone command:

  第一行中有一个『.』,它用来声明咱们所处的地位:即方才识别到了一个ZONETOK,目前正正在寻找一个『quotedname』。彰着,一个『quotedname』会以QUOTE着手,它将咱们转化到状况4。为进一步跟踪,用正在『调试』章节中提到的标记来编译example7。

  一朝YACC正告你产生了冲突,那么你的繁难来了。要处置这些题目显得是一种手艺情势,它会教会良多合于你的发言的东西。比你念知晓的要多的多的实质。题目缭绕于若何来翻译一系列标识符。假定咱们界说了一种发言,它须要经受一下两种敕令:

  也许你依然嗅到了繁难的滋味。状况机从读取单词『word』着手,接着它按照下一个标识符感应转换到何种状况。接下来的标识符可能是『mode』,它指定了若何来删除heater,或者是将要删除的heater。然而这里的繁难是,对待这两个敕令,下一个标识符都将是一个WORD。YACC是以也无法决计下一步该干嘛。这回导致一个『reduce/reduce』正告,而下一个正告即是『delete_a_heater』节点正在状况转化图中始终也不行抵达。这种状况的冲突容易处置(比方,重定名第一个敕令为『delete all heater』,或者将『all』行为一个隔离的标识符),但有时,(要处置冲突)却很是繁难。 通过『--verbose』参数天生的y.output文献可能供应给你极大的帮帮。

  GNU YACC(Bison)有一个很是棒的件,正在个中很好的记实了YACC的语法。个中只提到了一次LEX,然而它仍旧很棒的。你可能用Emacs中谁人很是好的『pinfo』东西阅读o文献。同时,正在Bison的主页上可能得到它:Bison手册。FLEX有一个不错的主页。假设你粗糙解析了FLEX所作所为,那将好坏常有益的。FLEX的手册也可能联机获取。正在这些合于YACC和LEX的先容之后,你恐怕感应你念须要更多的消息。下面的书本咱们都还没有阅读,但他们听起来不错:

新闻推荐

友情链接:

在线客服 : 服务热线:400-123-4567 电子邮箱: admin@webmoolah.com

公司地址:pp电子广东省广州市天河区某某工业园88号

PP电子官方网站是顶级下注平台,提供PP电子APP网站,PP电子最新官网,PP电子app下载,各种娱乐品种应有尽有,PP电子网站官方客服24小时在线为...

Copyright © 2012-2024 PP电子官方网站 版权所有