联系方式    |    在线留言 您好,金沙游戏电子欢迎访问这里是您的网站名称官网!
金沙游戏电子 - (中国)股份有限公司
客服热线400-123-4567
公司新闻

Lex与YACC详解

作者:小编    发布时间:2024-04-09 21:55:00    浏览量:

  只消你正在Unix处境中写进步调,你肯定会相遇怪异的Lex&YACC,就如GNU/Linux用户所熟知的Flex&Bison,这里的Flex即是由Vern Paxon完毕的一个Lex,Bison则是GNU版本的YACC。正在此咱们将团结称谓这些步调为Lex和YACC。新版本的步调是向上兼容的(译注:即兼容老版本),是以你能够用Flex和Bison来实验下咱们的实例。这些步调适用性极广恒温器,但好像你的C编译器相通,正在其主页上并没有刻画它们,也没相合于何如应用的消息。当和Lex团结应用时,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标识符构成,它后面随着一个形态,该形态正在LEX中界说Lex与YACC详解,为『on』或『off』。target_set稍微有点庞杂,它由TARGET标识符、TEMPERATURE以及一个数字构成。前面的谁人例子只要YACC文献的语法部门,肇始正在YACC文献中另有其它实质,完备的YACC文献如下:

  函数yyerror()正在YACC觉察一个谬误的时辰被移用,咱们只是简略的输出谬误消息,但实在还能够做极少更美丽的事宜,参见文档尾的『进阶阅读』部门。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),这将消费豪爽内存。是以,正在解析长的语句时,务必应用左递归,比方扫数文献。但有时难以避免右递归,然而,倘使你的语句并不太长,你就没有须要越轨应用左递归。倘使你有极少东西来终结(所以而决裂)你的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声明,YACC自愿选拔了union中的’string’成员。幼心这里$2中存储的一份拷贝,正在后面它会告诉用户发送死令到哪个heater(须要正在C文献头界说char *heater):

  许多处境下,咱们不生机从轨范输入解析,而生机解析给定的字符串。完毕伎俩是自界说完毕YY_INPUT,详细做法如下:

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

  这将天生一个y.output文献,个中阐理解所创修的谁人形态机。当你运转谁人天生的二进造文献,它将输出许多运转时消息。内里包蕴如今所运转的形态机以及读取到的极少标识符。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@qchygc.com

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

欢迎使用金沙游戏电子是中国综合娱乐公司,最安全的体育平台产品广泛,信誉第一.每天为您提供:金沙游戏电子官方网站,金沙游戏电子app,金沙游戏电子官方平...

Copyright © 2012-2024 金沙游戏电子 - (中国)股份有限公司 版权所有