﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>C++博客-菊花飘香-随笔分类-Lex学习</title><link>http://www.cppblog.com/Plator/category/7159.html</link><description>学术讨论博客</description><language>zh-cn</language><lastBuildDate>Wed, 28 May 2008 07:25:14 GMT</lastBuildDate><pubDate>Wed, 28 May 2008 07:25:14 GMT</pubDate><ttl>60</ttl><item><title>第一次使用flex</title><link>http://www.cppblog.com/Plator/archive/2008/05/28/51366.html</link><dc:creator>菊馨</dc:creator><author>菊馨</author><pubDate>Wed, 28 May 2008 02:47:00 GMT</pubDate><guid>http://www.cppblog.com/Plator/archive/2008/05/28/51366.html</guid><wfw:comment>http://www.cppblog.com/Plator/comments/51366.html</wfw:comment><comments>http://www.cppblog.com/Plator/archive/2008/05/28/51366.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.cppblog.com/Plator/comments/commentRss/51366.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Plator/services/trackbacks/51366.html</trackback:ping><description><![CDATA[<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt; TEXT-INDENT: 23.2pt; mso-char-indent-count: 2.21"><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">经老师介绍，而且最近进行实验二（将正则表达式转换为</span><span lang=EN-US>DFA</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">，然后转换为代码；想做一个类似于</span><span lang=EN-US>flex</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">的软件）觉得</span><span lang=EN-US>flex</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">很奇妙，输入一个正则表达式就能够输出对应的扫描程序，这也开始真正体现老师说的自动化。我一直对计算机的一个终极问题（&#8220;什么能够被有效地自动化&#8221;）很感兴趣，因此想先从</span><span lang=EN-US>flex</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">开始对这方面有点感性的认识：</span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt; TEXT-INDENT: 23.2pt; mso-char-indent-count: 2.21"><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">在网上搜索</span><span lang=EN-US>flex</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">，安装</span><span lang=EN-US>flex</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">，先下载</span><span lang=EN-US>flex</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">（原本打算放上来的，但是考虑到版权的问题，还是不要了。需要的朋友，我可以发给你），然后按照默认的步骤逐步安装。</span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt; TEXT-INDENT: 23.2pt; mso-char-indent-count: 2.21"><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">安装后，设置环境变量，将</span><span lang=EN-US>Path</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">指向</span><span lang=EN-US>flex.exe</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">所在的文件夹（本机上为：</span><span lang=EN-US>C:\Program Files\GnuWin32\bin</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">，一般按默认方式安装后</span><span lang=EN-US>flex.exe</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">都在该文件夹内），具体步骤：</span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt; TEXT-INDENT: 23.2pt; mso-char-indent-count: 2.21"><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">&#8594;对&#8220;我的电脑&#8221;图标按右键&#8594;选择&#8220;属性&#8221;&#8594;选择&#8220;高级&#8221;&#8594;单击&#8220;环境变量&#8221;&#8594;在&#8220;系统变量&#8221;中查找</span><span lang=EN-US>Path</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">项，并选择之&#8594;按&#8220;编辑&#8221;&#8594;在&#8220;变量值&#8221;的最后一项添加&#8220;</span><span lang=EN-US>;C:\Program Files\GnuWin32\bin</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">&#8221;，按确定完成。</span></p>
<div align=center src_cetemp="/images/cppblog_com/plator/first1.jpg"><img src="http://www.cppblog.com/images/cppblog_com/plator/first1.jpg" border=0></div>
<br>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt; TEXT-INDENT: 23.2pt; mso-char-indent-count: 2.21"><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">最近我是先从课本</span><span lang=EN-US>TINY</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">语言开始，</span><span lang=EN-US>TINY</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">语言的</span><span lang=EN-US>lex</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">文件在源代码的</span><span lang=EN-US>LEX</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">文件夹内</span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt 18pt; TEXT-INDENT: -18pt; mso-list: l0 level1 lfo1; tab-stops: list 18.0pt"><span lang=EN-US style="mso-fareast-font-family: 'Times New Roman'"><span style="mso-list: Ignore">1.<span style="FONT: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">在</span><span lang=EN-US>tiny.l</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">的最后添加：</span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US>int yywrap()</span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US>{</span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US>return 1;</span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US>}</span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-ascii-font-family: 'Comic Sans MS'; mso-hansi-font-family: 'Comic Sans MS'; mso-bidi-font-size: 10.5pt">用来结束扫描</span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt 18pt; TEXT-INDENT: -18pt; mso-list: l0 level1 lfo1; tab-stops: list 18.0pt"><span lang=EN-US style="mso-fareast-font-family: 'Times New Roman'"><span style="mso-list: Ignore">2.<span style="FONT: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">在</span><span lang=EN-US>console</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">上输入</span><span lang=EN-US>flex tiny.l</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">，生成</span><span lang=EN-US>lex.yy.c</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">，将其替换</span><span lang=EN-US>scan.c</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">，编译链接生成</span><span lang=EN-US>tiny</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">的编译器。</span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US><o:p>&nbsp;</o:p></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">参考文献：</span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US><a href="http://course.cugnc.com/bianyi/shiyan/CHAPTER/f1.htm"><font color=#0000ff><u>http://course.cugnc.com/bianyi/shiyan/CHAPTER/f1.htm</u></font></a></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US><a href="http://blog.csdn.net/litchh/archive/2004/07/14/40983.aspx"><font color=#0000ff><u>http://blog.csdn.net/litchh/archive/2004/07/14/40983.aspx</u></font></a></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US><a href="http://www.cnscn.org/read.php?tid-10862.html"><u><font color=#0000ff>http://www.cnscn.org/read.php?tid-10862.html</font></u></a></span></p>
<img src ="http://www.cppblog.com/Plator/aggbug/51366.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Plator/" target="_blank">菊馨</a> 2008-05-28 10:47 <a href="http://www.cppblog.com/Plator/archive/2008/05/28/51366.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>MFC与LEX 结合要注意的问题</title><link>http://www.cppblog.com/Plator/archive/2008/05/24/50983.html</link><dc:creator>菊馨</dc:creator><author>菊馨</author><pubDate>Sat, 24 May 2008 15:33:00 GMT</pubDate><guid>http://www.cppblog.com/Plator/archive/2008/05/24/50983.html</guid><wfw:comment>http://www.cppblog.com/Plator/comments/50983.html</wfw:comment><comments>http://www.cppblog.com/Plator/archive/2008/05/24/50983.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/Plator/comments/commentRss/50983.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Plator/services/trackbacks/50983.html</trackback:ping><description><![CDATA[<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">由于</span><span lang=EN-US>flex</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">往往为我们生成的是</span><span lang=EN-US>C</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">代码（</span><span lang=EN-US>lex.yy.c</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">），而实际上我们通常要把它们应用到</span><span lang=EN-US>C++</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">中，特别是应用到</span><span lang=EN-US>Windows</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">应用程序中来，在</span><span lang=EN-US>MFC</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">工程下构造词法分析程序。</span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">我们往往只是将产生的</span><span lang=EN-US>lex.yy.c</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">直接添加到</span><span lang=EN-US>MFC</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">工程就完事，但是编译时，由于各种各样的原因，而产生大量的语法错误，难以修改。本人最近在</span><span lang=EN-US>MFC</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">下构造一个扩充</span><span lang=EN-US>TINY</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">语言的词法分析也是遇到了一些问题，查阅了大量资料（已经将主要的参考资料放到</span><span lang=EN-US>lex</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">学习栏目上，请看：<a href="http://www.cppblog.com/Plator/category/7159.html">http://www.cppblog.com/Plator/category/7159.html</a>），耗费了我两天的时间解决这些问题。因此我想写本文，将把一些注意问题叙述如下，希望对一些朋友有所帮助。</span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US><o:p>&nbsp;</o:p></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt 18pt; TEXT-INDENT: -18pt; mso-list: l0 level1 lfo1; tab-stops: list 18.0pt"><span lang=EN-US style="mso-fareast-font-family: 'Times New Roman'"><span style="mso-list: Ignore">1.<span style="FONT: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">要将</span><span lang=EN-US>lex.yy.c</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">改为</span><span lang=EN-US>CPP</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">文件：</span><span lang=EN-US>lex.yy.cpp</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">。因为</span><span lang=EN-US>MFC</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">是</span><span lang=EN-US>C++</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">工程，若不修改则会出现错误；</span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt 18pt; TEXT-INDENT: -18pt; mso-list: l0 level1 lfo1; tab-stops: list 18.0pt"><span lang=EN-US style="mso-fareast-font-family: 'Times New Roman'"><span style="mso-list: Ignore">2.<span style="FONT: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">将</span><span lang=EN-US>#include &lt;stdio.h&gt;</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">改为</span><span lang=EN-US>#include &lt;stdafx.h&gt;</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">，不然会出现如下错误：</span><span lang=EN-US>unexpected end of file while looking for precompiled header directive</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">；</span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt 18pt; TEXT-INDENT: -18pt; mso-list: l0 level1 lfo1; tab-stops: list 18.0pt"><span lang=EN-US style="mso-fareast-font-family: 'Times New Roman'"><span style="mso-list: Ignore">3.<span style="FONT: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">将</span><span lang=EN-US>flex</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">安装目录下</span><span lang=EN-US>include</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">文件夹的</span><span lang=EN-US>unistd.h</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">添加到</span><span lang=EN-US>MFC</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">工程内，并修改</span><span lang=EN-US>#include &lt;unistd.h&gt;</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">，为</span><span lang=EN-US>#include "unistd.h"</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">。因为</span><span lang=EN-US>unistd.h</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">文件中定义了词法分析需要用到一些头文件。</span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt 18pt; TEXT-INDENT: -18pt; mso-list: l0 level1 lfo1; tab-stops: list 18.0pt"><span lang=EN-US style="mso-fareast-font-family: 'Times New Roman'"><span style="mso-list: Ignore">4.<span style="FONT: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">如果</span><span lang=EN-US>lex</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">源文件需要用</span><span lang=EN-US>input</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">读取字符，则应该换用</span><span lang=EN-US>yyinput</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">，因为</span><span lang=EN-US>input</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">会与</span><span lang=EN-US>C++</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">中的流名词重复，会出现编译错误；</span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt 18pt; TEXT-INDENT: -18pt; mso-list: l0 level1 lfo1; tab-stops: list 18.0pt"><span lang=EN-US style="mso-fareast-font-family: 'Times New Roman'"><span style="mso-list: Ignore">5.<span style="FONT: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">注意一些</span><span lang=EN-US>I/O</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">例程的应用，具体请见：<a href="http://www.cppblog.com/Plator/archive/2008/05/24/50940.html">http://www.cppblog.com/Plator/archive/2008/05/24/50940.html</a></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US><o:p>&nbsp;</o:p></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">只是短短几个错误，但是解决的过程参考了大量资料，虽然不知道这些资料出自何人之手，但是在此感谢这些作者！</span></p>
<img src ="http://www.cppblog.com/Plator/aggbug/50983.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Plator/" target="_blank">菊馨</a> 2008-05-24 23:33 <a href="http://www.cppblog.com/Plator/archive/2008/05/24/50983.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Lex 入门 [转载]</title><link>http://www.cppblog.com/Plator/archive/2008/05/24/50982.html</link><dc:creator>菊馨</dc:creator><author>菊馨</author><pubDate>Sat, 24 May 2008 15:28:00 GMT</pubDate><guid>http://www.cppblog.com/Plator/archive/2008/05/24/50982.html</guid><wfw:comment>http://www.cppblog.com/Plator/comments/50982.html</wfw:comment><comments>http://www.cppblog.com/Plator/archive/2008/05/24/50982.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/Plator/comments/commentRss/50982.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Plator/services/trackbacks/50982.html</trackback:ping><description><![CDATA[<p>First! <br>lex程序的结构是这样的！</p>
<p>定义<br>%%<br>规则<br>%%<br>用户代码</p>
<p>&nbsp;</p>
<p>一个 Lex 程序分为三个段：第一段是 C 和 Lex 的全局声明，第二段包括模式（C 代码），第三段是补充的 C 函数。 这些段以%%来分界。 下面是一个行数与字数的统计工具。</p>
<p><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int num_lines = 0, num_chars = 0;<br>&nbsp;%%<br>\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ++num_lines; ++num_chars;<br>.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ++num_chars;</p>
<p>%%<br>main()<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; yylex();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; printf( "# of lines = %d, # of chars = %d\n",<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; num_lines, num_chars );<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;</p>
<p>Second! <br>对First内容的回顾 <br>C 和 Lex 的全局声明 <br>这一段中我们可以增加 C 变量声明。这里我们将为字数统计程序声明一个整型变量，来保存程序统计出来的字数。我们还将进行 Lex 的标记声明。 </p>
<p>字数统计程序的声明</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; %{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int wordCount = 0;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; %}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; chars [A-za-z\_\'\.\"]<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; numbers ([0-9])+<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; delim [" "\n\t]<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; whitespace {delim}+<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; words {chars}+<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; %%</p>
<p>&nbsp; </p>
<p>两个百分号标记指出了 Lex 程序中这一段的结束和三段中第二段的开始。 </p>
<p>Lex 的模式匹配规则 <br>让我们看一下 Lex 描述我们所要匹配的标记的规则。（我们将使用 C 来定义标记匹配后的动作。）继续看我们的字数统计程序，下面是标记匹配的规则。 <br>字数统计程序中的 Lex 规则</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {words} { wordCount++; /*<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; increase the word count by one*/ }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {whitespace} { /* do<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; nothing*/ }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {numbers} { /* one may<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; want to add some processing here*/ }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; %%</p>
<p>&nbsp; <br>C 代码 <br>Lex 编程的第三段，也就是最后一段覆盖了 C 的函数声明（有时是主函数）。注意这一段必须包括 yywrap() 函数。 Lex 有一套可供使用的函数和变量。 其中之一就是 yywrap。一般来说，yywrap() 的定义如下例。我们将在 高级 Lex 中探讨这一问题。 <br>字数统计程序的 C 代码段</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; void main()<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; yylex(); /* start the<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; analysis*/<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; printf(" No of words:<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; %d\n", wordCount);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int yywrap()<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return 1;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp; </p>
<p>Lex 编程的基本元素就这样搞定了，它将帮助你编写简单的词法分析程序。 <br>Third <br>高级Lex <br>Lex 有几个函数和变量提供了不同的信息，可以用来编译实现复杂函数的程序。下表中列出了一些变量和函数，以及它们的使用。 详尽的列表请参考 Lex 手册。 <br>Lex 变量 <br>&nbsp;&nbsp; yyin&nbsp; FILE* 类型。 它指向 lexer 正在解析的当前文件。<br>&nbsp;&nbsp; yyout FILE* 类型。 它指向记录 lexer 输出的位置。 缺省情况下，yyin 和 yyout 都指向标准输入和输出。<br>&nbsp;&nbsp; yytext 匹配模式的文本存储在这一变量中（char*）。<br>&nbsp;&nbsp; yyleng 给出匹配模式的长度。<br>&nbsp;&nbsp; yylineno 提供当前的行数信息。（lexer不一定支持。） </p>
<p>Lex 函数 <br>&nbsp;&nbsp; yylex() 这一函数开始分析。 它由 Lex 自动生成。<br>&nbsp;&nbsp; yywrap() 这一函数在文件（或输入）的末尾调用。如果函数的返回值是1，就停止解析。 因此它可以用来解析多个文件。代码可以写在第三段，这就能够解析多个文件。 方法是使用 yyin 文件指针（见上表）指向不同的文件，直到所有的文件都被解析。最后，yywrap() 可以返回 1 来表示解析的结束。<br>&nbsp;&nbsp; yyless(int n) 这一函数可以用来送回除了前 n? 个字符外的所有读出标记。<br>&nbsp;&nbsp; yymore() 这一函数告诉 Lexer 将下一个标记附加到当前标记后。 <br>到此为止，可能你看到lex程序还会范晕，没关系，下面我们接着来，分析一个类pascal语法的极简析器！ <br>/* 这个就是注释了*/<br>&nbsp; /* scanner for a toy Pascal-like language */ <br>申明部分开始<br>%{ 内的东西会原封不动地出现在输出文件中 }%</p>
<p>&nbsp; %{<br>&nbsp;&nbsp;&nbsp;&nbsp; /* need this for the call to atof() below */<br>&nbsp;&nbsp;&nbsp;&nbsp; #include &lt;math.h&gt;<br>&nbsp; %}<br>&nbsp; DIGIT&nbsp;&nbsp;&nbsp; [0-9]<br>&nbsp; ID&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [a-z][a-z0-9]* <br>&nbsp; %% <br>模式部分开始 <br>&nbsp; {DIGIT}+&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp; printf( "An integer: %s (%d)\n", yytext,<br>&nbsp;&nbsp;&nbsp; atoi( yytext ) );<br>&nbsp; } <br>&nbsp; {DIGIT}+"."{DIGIT}*&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp; printf( "A float: %s (%g)\n", yytext,<br>&nbsp;&nbsp;&nbsp; atof( yytext ) );<br>&nbsp; } <br>&nbsp; if|then|begin|end|procedure|function&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp; printf( "A keyword: %s\n", yytext );<br>&nbsp; } <br>&nbsp; {ID}&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; printf( "An identifier: %s\n", yytext ); <br>&nbsp; "+"|"-"|"*"|"/"&nbsp;&nbsp; printf( "An operator: %s\n", yytext );<br>&nbsp; "{"[^}\n]*"}"&nbsp;&nbsp;&nbsp;&nbsp; /* eat up one-line comments */ <br>&nbsp; [ \t\n]+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* eat up whitespace */<br>&nbsp; .&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; printf( "Unrecognized character: %s\n", yytext ); <br>&nbsp; %% <br>补充部分开始 <br>&nbsp; main( argc, argv )<br>&nbsp;&nbsp; int argc;<br>&nbsp;&nbsp; char **argv;<br>&nbsp; {<br>&nbsp;&nbsp;&nbsp; ++argv, --argc;&nbsp; /* skip over program name */<br>&nbsp;&nbsp;&nbsp; if ( argc &gt; 0 )<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; yyin = fopen( argv[0], "r" );<br>&nbsp;&nbsp;&nbsp; else<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; yyin = stdin;<br>&nbsp;&nbsp;&nbsp; yylex();<br>&nbsp; } <br>想要真正了解lex, [[正则表达式]] 是关键! <br>Four <br>yytext 匹配模式的文本存储变量, 可以通过在申明阶段使用%pointer或%array来控制是一个字符指针还是一个字符数组。指针模式与数组模式各有特点，导致在yytex申明上也不一样，具体请参考lex手册！ <br>在模式阶段中</p>
<p>&nbsp; 模式&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 动作<br>&nbsp; [ \t]+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; putchar( ' ' );<br>&nbsp; [ \t]+$&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* ignore this token */</p>
<p>模式部分是正则表达式，动作部分是处理方法，动作部分如果时{开头，那么，动作将会持续到},如果动作中出现了括号{},开始采用 %{ %}来表示动作去区段。动作部分如果时 |,就表示与下一条规则执行相同的动作。 <br>好的，我们来看一个更为实用一点的lex程序。<br>我们先定义三个动作:<br>ECHO 将yytext输出<br>BEGIN 开始一个条件处理块<br>REJECT 指示简析器对当前规则不做处理，而是采用第二匹配规则。 <br>&nbsp; int word_count = 0; <br>&nbsp; %% <br>&nbsp; frob&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; special(); REJECT;<br>&nbsp; [^ \t\n]+&nbsp;&nbsp; ++word_count; <br>如果frob没有REJECT动作，frob将不会被计数，因为解析器在通常情况下，每个被匹配的对象只会对一个动作生效，多个REJECT也是允许的，会寻找下一个最配的规则来做处理。所以，下面的规则会把输入的"abcd"处理后输出"abcdabcaba". <br>&nbsp; %%<br>&nbsp; a&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |<br>&nbsp; ab&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |<br>&nbsp; abc&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |<br>&nbsp; abcd&nbsp;&nbsp;&nbsp;&nbsp; ECHO; REJECT;<br>&nbsp; .|\n&nbsp;&nbsp;&nbsp;&nbsp; /* eat up any unmatched character */ </p>
<p>`yymore()' 告诉解析器下一次匹配的规则，满足的部分将会添加到当前yytext值得后面而不是替换它。 例如，指定的输入"mega-kludge"经过下面的程序处理后将会输出"mega-mega-kludge"。 <br>&nbsp; %%<br>&nbsp; mega-&nbsp;&nbsp;&nbsp; ECHO; yymore();<br>&nbsp; kludge&nbsp;&nbsp; ECHO; <br>第一个 "mega-" 被满足并且输出. 然后 "kludge" 满足, 但是并没有替换之前的"mega-"而是"kludge"附加到他的后面，然后输出的其实是"mega-kludge". <br>yymore()需要两件事情需要注意。第一，yymnore()依赖于表现当前匹配项的长度yyleng的值，所以使用yymore不允许改变yyleng的值。第二，yymore()的使用会使解析器付出一点点性能的代价。 <br>有yymore()就有yyless() <br>yyless(n) 返回当前匹配项除了开始的n个字符内的所有的内容到输入缓存区，解析器处理下一个匹配时，它们将会被重新解析。yyless将会导致yytext与yyleng的调整。（yyleng将会等于=n） 如输入"foobar"被下面的程序处理后，将会输出"boobarbar". 因为前n=3个字符foo外的字符bar被重新返回到输入缓存区了。 <br>&nbsp; %%<br>&nbsp; foobar&nbsp;&nbsp;&nbsp; ECHO; yyless(3);<br>&nbsp; [a-z]+&nbsp;&nbsp;&nbsp; ECHO; <br>参数0对于yyless将会导致整个当前匹配将会被重新解析。除非你改变了解析器本来的处理流程(如使用begin),这将会导致循环结束。需要注意的是，yyless是一个宏，并且在flex输入文件中使用，不能在其他源文件中使用。 <br>unput(c) 将字符c放回到输入流中，该字符可以重新被解析。下面的动作将当前的匹配值附上括号后重新进行匹配。 <br>&nbsp;{<br>&nbsp; int i;<br>&nbsp; /* Copy yytext because unput() trashes yytext */<br>&nbsp; char *yycopy = strdup( yytext );<br>&nbsp; unput( ')' );<br>&nbsp; for ( i = yyleng - 1; i &gt;= 0; --i )<br>&nbsp;&nbsp;&nbsp; unput( yycopy[i] );<br>&nbsp; unput( '(' );<br>&nbsp; free( yycopy );<br>&nbsp;} <br>注意: 由于每次unput()将指定的字符添加到输入源的开头，所以将字符串添加到输入源开头必须从后道前处理。一个比较重要的潜在问题是使用unput()的时候，如果采用了%pointer指针模式保存yytext,unput会破坏yytext的内容，从最右边的字符开始将会破坏左边的一个字符。如果在unput()后要用到yytext,你首先必须复制一份yytext,或者用%array模式来保存yytext. 最后你不能放一个EOF去试图标志输入流的结束。 <br>input 从输入源中读取下一个字符。例如，下面有的例子将会吃掉C语言注释</p>
<p>&nbsp; %%<br>&nbsp; "/*"&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; register int c;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for ( ; ; )<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; while ( (c = input()) != '*' &amp;&amp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; c != EOF )<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;&nbsp;&nbsp;&nbsp; /* eat up text of comment */ <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if ( c == '*' )<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; while ( (c = input()) == '*' )<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if ( c == '/' )<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; break;&nbsp;&nbsp;&nbsp; /* found the end */<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if ( c == EOF )<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; error( "EOF in comment" );<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; break;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>注意: 如果简析器采用用C++编译，input()被yyinput()的替代，因为input()与C++中的流名称input冲突。 <br>YY_FLUSH_BUFFER 刷新解析器内部缓存以便于下一次的匹配工作，首先它会使用YY_INPUT填充缓存区。这是通用yy_flush_buffer()的一个特例，将会在多输入缓存中描述。 <br>yyterminate()可以在动作内部返回描述区域中使用，它将终止解析器并返回0给解析器调用者，表示操作完成。缺省情况下，到达文件结束位置也会被调用，它是一个宏，并且可能重定义。</p>
<p><br>Lex进阶 <br>模式<br>模式在第一阶段或第二个阶段使用，也就是在申明或规则阶段中出现，模式定义了匹配的目标，目标被匹配后将会执行动作。<br>对于模式不想做太多说明，使用正则表达式定义，可以参看 regex 或 pcre.</p>
<p>开始条件<br>lex提供了根据条件激活规则的机制。在&lt;sc&gt;前缀的规则将会在解析器在"sc"的开始条件下被匹配。</p>
<p>&lt;STRING&gt;[^"]*&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; { /* eat up the string body ... */&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ...&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>将会在启动条件"STRING"的情况下被激活。</p>
<p>&lt;INITIAL,STRING,QUOTE&gt;\.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; { /* handle an escape ... */&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ...&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>将会在 "INITIAL", "STRING", "QUOTE"三者之一的条件下被激活。</p>
<p>开始条件在输入源的定义(第一）部分被申明，在&#8216;%s' 或 &#8217;%x'后跟随着名字列表。 %s申明了包含的开始条件，%x申明了排他的开始条件。开始条件被BEGIN动作激活。直到下一个BEGIN动作，满足开始条件名称的规则将会被规则，不满足启动条件的规则将不会被执行。</p>
<p><br>如果是包含条件，没有开始条件的规则也会被激活执行，如果时排他条件，只有满足开始条件的规则才会被执行。</p>
<p>具有相同排他条件的规则的集合可以使解析器独立于其他的规则。因此，排他条件可以容易地创建微型解析器处理输入源中的独立与其他部分的一部分（如，注释）。如果对于包含与排他条件还有混淆，可以看下面的例子。</p>
<p>%s example%%&lt;example&gt;foo&nbsp;&nbsp; do_something();bar&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; something_else();<br>等同于</p>
<p><br>%x example%%&lt;example&gt;foo&nbsp;&nbsp; do_something();&lt;INITIAL,example&gt;bar&nbsp;&nbsp;&nbsp; something_else();<br>上面的程序中如果没有&lt;INITIAL,example&gt;，在example条件下bar规则将永远不会被激活。如果使用&lt;example&gt;，将会导致只能在exmaple开始条件下激活，而INITIAL条件下不会被激活。而第一个程序中在任何条件下bar都被会激活。因为第一个程序用example时%s，时包含条件。页可以通过特殊开始条件&lt;*&gt;来配置任何开始条件，上面的程序还可以写为：</p>
<p>%x example%%&lt;example&gt;foo&nbsp;&nbsp; do_something();&lt;*&gt;bar&nbsp;&nbsp;&nbsp; something_else();<br>缺省规则（显示任何未被匹配的字符）在开始条件下仍然生效。等同于：</p>
<p>&lt;*&gt;.|\\n&nbsp;&nbsp;&nbsp;&nbsp; ECHO;<br>&#8216;BEGIN(0)&#8217;在无开始条件的规则激活条件下返回原始状态，这个状态同于开始条件下的'INITIAL',所以&#8216;BEGIN(INITIAL)'等同于&#8217;BEGIN(0)'。<br>BEGIN行为在规则部分的开头是默认的代码（BEGIN actions can also be given as indented code at the beginning of the rules section.请翻译）例如，下面的代码将会仅需SPECIAL开始条件，不管合适yylex()被调用并且全局变量enter_special是true。</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int enter_special;%x SPECIAL%%&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if ( enter_special )&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; BEGIN(SPECIAL);&lt;SPECIAL&gt;blahblahblah...more rules follow...<br>为了说明开始条件，我们用两种方法处理"123.456".缺省将会被解析为 '123','.','456'三个标记，如果expect-floats后面将会被解析为浮点数 123.456</p>
<p>%{#include &lt;math.h&gt;%}%s expect%%expect-floats&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; BEGIN(expect);&lt;expect&gt;[0-9]+"."[0-9]+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; printf( "found a float, = %f\n",&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; atof( yytext ) );&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }&lt;expect&gt;\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* that's the end of the line, so&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; * we need another "expect-number"&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; * before we'll recognize any more&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; * numbers&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; */&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; BEGIN(INITIAL);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }[0-9]+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; printf( "found an integer, = %d\n",&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; atoi( yytext ) );&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }"."&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; printf( "found a dot\n" );<br>下面的代码能够是被C语言注释并且统计行数。</p>
<p>%x comment%%&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int line_num = 1;"/*"&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; BEGIN(comment);&lt;comment&gt;[^*\n]*&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* eat anything that's not a '*' */&lt;comment&gt;"*"+[^*/\n]*&nbsp;&nbsp; /* eat up '*'s not followed by '/'s */&lt;comment&gt;\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ++line_num;&lt;comment&gt;"*"+"/"&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; BEGIN(INITIAL);<br>实际上，编写高速解析程序的办法时在每个规则中做尽可能多的匹配。</p>
<p>This scanner goes to a bit of trouble to match as much text as possible with each rule. In general, when attempting to write a high-speed scanner try to match as much possible in each rule, as it's a big win. </p>
<p>注意: 开始条件的名字实际上时一个整形值并且能够被保存，所以，上面的代码可以扩展为：</p>
<p>%x comment foo%%&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int line_num = 1;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int comment_caller;"/*"&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; comment_caller = INITIAL;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; BEGIN(comment);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }...&lt;foo&gt;"/*"&nbsp;&nbsp;&nbsp; {&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; comment_caller = foo;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; BEGIN(comment);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }&lt;comment&gt;[^*\n]*&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* eat anything that's not a '*' */&lt;comment&gt;"*"+[^*/\n]*&nbsp;&nbsp; /* eat up '*'s not followed by '/'s */&lt;comment&gt;\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ++line_num;&lt;comment&gt;"*"+"/"&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; BEGIN(comment_caller);<br>而且，可能易使用YY_START宏来访问当前的开始条件。如上面的赋值条件可以改写为</p>
<p>comment_caller = YY_START</p>
<p>YYSTATE是YY_START的别名（因为AT&amp;T lex使用了YYSTATE）。<br>注意 开始条件没有他们的名字空间; %s 与 %x 申明与 #define形式一样。</p>
<p>到这里，时一个使用排他开始条件如何匹配C风格的引用字符串的处理。包含的扩展的转义，但不包括检查，因为代码太长。</p>
<p>%x str%%&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; char string_buf[MAX_STR_CONST];&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; char *string_buf_ptr;\"&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; string_buf_ptr = string_buf; BEGIN(str);&lt;str&gt;\"&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; { /* saw closing quote - all done */&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; BEGIN(INITIAL);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; *string_buf_ptr = '\0';&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* return string constant token type and&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; * value to parser&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; */&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }&lt;str&gt;\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* error - unterminated string constant */&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* generate error message */&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }&lt;str&gt;\\[0-7]{1,3} {&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* octal escape sequence */&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int result;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (void) sscanf( yytext + 1, "%o", &amp;result );&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if ( result &gt; 0xff )&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* error, constant is out-of-bounds */&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; *string_buf_ptr++ = result;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }&lt;str&gt;\\[0-9]+ {&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* generate error - bad escape sequence; something&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; * like '\48' or '\0777777'&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; */&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }&lt;str&gt;\\n&nbsp; *string_buf_ptr++ = '\n';&lt;str&gt;\\t&nbsp; *string_buf_ptr++ = '\t';&lt;str&gt;\\r&nbsp; *string_buf_ptr++ = '\r';&lt;str&gt;\\b&nbsp; *string_buf_ptr++ = '\b';&lt;str&gt;\\f&nbsp; *string_buf_ptr++ = '\f';&lt;str&gt;\\(.|\n)&nbsp; *string_buf_ptr++ = yytext[1];&lt;str&gt;[^\\\n\"]+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; char *yptr = yytext;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; while ( *yptr )&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; *string_buf_ptr++ = *yptr++;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>通常，如上面的例子中所看到你，会有许多相同开始条件的处理。开始条件范围可以简化重复操作。</p>
<p><br>&lt;SCs&gt;{}<br>SCs 是一个或开始条件的列表。在这个开始条件范围内，每个规则将会自动具有前缀 `&lt;SCs&gt;' 直到 `}' 与开始的 `{' 匹配.&nbsp; 例如 </p>
<p>&lt;ESC&gt;{&nbsp;&nbsp;&nbsp; "<a href="file://n/">\\n</a>"&nbsp;&nbsp; return '\n';&nbsp;&nbsp;&nbsp; "<a href="file://r/">\\r</a>"&nbsp;&nbsp; return '\r';&nbsp;&nbsp;&nbsp; "<a href="file://f/">\\f</a>"&nbsp;&nbsp; return '\f';&nbsp;&nbsp;&nbsp; "<a href="file://0/">\\0</a>"&nbsp;&nbsp; return '\0';}<br>等价于 </p>
<p>&lt;ESC&gt;"<a href="file://n/">\\n</a>"&nbsp; return '\n';&lt;ESC&gt;"<a href="file://r/">\\r</a>"&nbsp; return '\r';&lt;ESC&gt;"<a href="file://f/">\\f</a>"&nbsp; return '\f';&lt;ESC&gt;"<a href="file://0/">\\0</a>"&nbsp; return '\0';<br>开始条件页可以嵌套，下面时三个管理开始条件堆栈的参数。 </p>
<p>`void yy_push_state(int new_state)' <br>将当前的开始条件压栈，切换到 new_state 与使用 `BEGIN new_state'类似。 <br>`void yy_pop_state()' <br>从栈顶弹出，类似于 BEGIN. <br>`int yy_top_state()' <br>返回栈顶值，不改变栈内容。 <br>开始条件栈动态增长，没有固定限制，如果内容用尽，程序竟会终止。 </p>
<p>为了使用开始条件栈，需要使用 `%option stack' 指令。 </p>
<p>&nbsp;</p>
<p><br>多输入缓存区 </p>
<p>&nbsp;</p>
<p>一些允许include文件解析器的解析器要求从几个输入流中读取内容。YY_INPUT只在结束缓存时被调用，碰到 include 后需要切换输入源，而解析一个描述也许需要很长时间。为了解决此类问题，解析器提供了创建并在多个输入缓存中创建的机制。输入缓存可以通过下面的方式创建: </p>
<p>YY_BUFFER_STATE yy_create_buffer( FILE *file, int size )</p>
<p><br>参数为与缓存关联的输入文件指针，以及足够的可维持size字符（如果不确定，size可以使用YY_BUF_SIZE)。返回一个YY_BUFFER_STATE,可以传递到其他的处理过程。YY_BUFFER_STATE是一个不可见结构yy_buffer_state的指针，所以可以安全地使用`((YY_BUFFER_STATE) 0)'来初始化YY_BUFFER_STATE，如果你愿意，你可以在解析器之外的源程序中引用这个不透明结构来正确的申明输入缓存。可以通过下面的参数来选择一个缓存区。 </p>
<p>void yy_switch_to_buffer( YY_BUFFER_STATE new_buffer )</p>
<p><br>切换解析器的输入缓存将会导致记接下来的匹配项来自于新的缓存中。yy_switch_to_buffer可能出现在yywrap中为继续解析做准备，替换打开一个新的文件并执行yyin. 通过yy_switch_to_buffer 或 yywrap切换输入源不改变开始条件。 </p>
<p>&nbsp;</p>
<p>void yy_delete_buffer( YY_BUFFER_STATE buffer )</p>
<p><br>用于收回与缓存关联的空间。你可以使用下面的函数清空当前内容:</p>
<p>void yy_flush_buffer( YY_BUFFER_STATE buffer )</p>
<p><br>此函数废弃缓存内容，下一个解析器试图匹配一个内容时将会使用YY_INPUT来更新缓存区。</p>
<p>`yy_new_buffer()' 是 `yy_create_buffer()' 的一个别名，用于提供C++使用new 与 delete操作创建与销毁动态对象的兼容性。</p>
<p>最后, YY_CURRENT_BUFFER 宏返回 YY_BUFFER_STATE 指针，表示当前的缓存。</p>
<p>这里是一个扩展include使用的一个解析器 (`&lt;&lt;EOF&gt;&gt;' 特性将会在以后讨论):</p>
<p><br>/* "incl" 状态用于获取include的文件名 */<br>%x incl</p>
<p>%{<br>#define MAX_INCLUDE_DEPTH 10<br>YY_BUFFER_STATE include_stack[MAX_INCLUDE_DEPTH];<br>int include_stack_ptr = 0;<br>%}</p>
<p>%%<br>include&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; BEGIN(incl);</p>
<p>[a-z]+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ECHO;<br>[^a-z\n]*\n?&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ECHO;</p>
<p>&lt;incl&gt;[ \t]*&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* eat the whitespace */<br>&lt;incl&gt;[^ \t\n]+&nbsp;&nbsp; { /* got the include file name */<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if ( include_stack_ptr &gt;= MAX_INCLUDE_DEPTH )<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fprintf( stderr, "Includes nested too deeply" );<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; exit( 1 );<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; include_stack[include_stack_ptr++] =<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; YY_CURRENT_BUFFER;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; yyin = fopen( yytext, "r" );</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if ( ! yyin )<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; error( ... );</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; yy_switch_to_buffer(<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; yy_create_buffer( yyin, YY_BUF_SIZE ) );</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; BEGIN(INITIAL);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&lt;&lt;EOF&gt;&gt; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if ( --include_stack_ptr &lt; 0 )<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; yyterminate();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; yy_delete_buffer( YY_CURRENT_BUFFER );<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; yy_switch_to_buffer(<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; include_stack[include_stack_ptr] );<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p><br>提供三个过程来实现内存字符串而不是文件输入缓存的解析。它们都要创建一个输入缓存来解析字符串，并且返回YY_BUFFER_STATE (可以在完成解析后用 `yy_delete_buffer()' 删除).，也可以通过`yy_switch_to_buffer()'来切换, 下一次调用`yylex()' 将会解析字符串。</p>
<p>`yy_scan_string(const char *str)'&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 解析0结尾字符串。<br>`yy_scan_bytes(const char *bytes, int len)'&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 解析bytes开始的len个字符(可能包含 0 字符)</p>
<p><br>注意，上面的两个函数会创建字符串或字节串的副本。(这也许时期望的，因为`yylex()' 会修改被解析缓存的内容)&nbsp; 可以使用下面的方式来拒绝使用副本:</p>
<p>`yy_scan_buffer(char *base, yy_size_t size)'</p>
<p>将会从base开始解析，包含size个字节, 最后的两个字节必须是 YY_END_OF_BUFFER_CHAR (ASCII NUL)。他们不会被解析, 解析范围从 `base[0]'&nbsp; 到 `base[size-2]'（包含）。如果你没能按照这种规定使用base（如，忘记了最后的两个YY_END_OF_BUFFER_CHAR字节), `yy_scan_buffer()' 将会返回空指针而不创建YY_BUFFER_STATE。yy_size_t类型是个整型，可以转化为整数来反映buffer的长度。</p>
<p>&nbsp;</p>
<p><br>文件结束规则</p>
<p><br>特殊规则 "&lt;&lt;EOF&gt;&gt;" 只是规则在文件结束位置发生且yywrap()返回非0值。(如，没有更多的文件要处理). 这个动作必须完成下面四件事情之一:</p>
<p>赋值给yyin一个新的文件 (早期版本的flex, 此操作后必须调用特殊动作 YY_NEW_FILE; 这个操作已经不需要了);<br>执行一个返回申明;<br>执行一个特殊的`yyterminate()' 动作;<br>或者使用`yy_switch_to_buffer()' 切换到一个新的输入缓存区.</p>
<p><br>&lt;&lt;EOF&gt;&gt; 不能与其他模式一起使用；它也许仅在开始条件列表申明。如果指定了不合法 &lt;&lt;EOF&gt;&gt; 规则, 它将会应用到所有的开始条件而不仅是 &lt;&lt;EOF&gt;&gt; 动作. 指定 &lt;&lt;EOF&gt;&gt; 规则仅在 initial 开始条件下匹配，就是用:</p>
<p>&lt;INITIAL&gt;&lt;&lt;EOF&gt;&gt;</p>
<p><br>下面的规则可以发现象不关闭的注释类的问题。</p>
<p>%x quote<br>%%</p>
<p>...other rules for dealing with quotes...</p>
<p>&lt;quote&gt;&lt;&lt;EOF&gt;&gt;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; error( "unterminated quote" );<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; yyterminate();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&lt;&lt;EOF&gt;&gt;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if ( *++filelist )<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; yyin = fopen( *filelist, "r" );<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; yyterminate();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p><br>&nbsp;</p>
<img src ="http://www.cppblog.com/Plator/aggbug/50982.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Plator/" target="_blank">菊馨</a> 2008-05-24 23:28 <a href="http://www.cppblog.com/Plator/archive/2008/05/24/50982.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Flex内存泄露问题[转载]</title><link>http://www.cppblog.com/Plator/archive/2008/05/24/50981.html</link><dc:creator>菊馨</dc:creator><author>菊馨</author><pubDate>Sat, 24 May 2008 15:25:00 GMT</pubDate><guid>http://www.cppblog.com/Plator/archive/2008/05/24/50981.html</guid><wfw:comment>http://www.cppblog.com/Plator/comments/50981.html</wfw:comment><comments>http://www.cppblog.com/Plator/archive/2008/05/24/50981.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/Plator/comments/commentRss/50981.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Plator/services/trackbacks/50981.html</trackback:ping><description><![CDATA[最近弄数据仓库的元数据，这工作里面的一项重头戏就是解析SQL语句。由于数据仓库的数据加工逻辑比较复杂，成百上千行的SQL随处可见，因此如何把其中的数据来源与去向清晰的整理出来是非常重要的，以前我解析SQL语句用C++自己写，能实现部分功能，这次用的工具是Flex和Bison，学过编译原理的都知道大名鼎鼎的Lex/yacc这两个工具，Flex和Bison就是Lex/yacc的windows版本，Flex是解析词法的，Bison是用来解析语法的。<br>我写了一个Flex的例子测试，这个例子能把SQL语句群按照分号隔开，放入一个list，并且读出每一句SQL的起始和结束的位置，以及该SQL的类型，例如是一个空SQL还是只含注释的SQL，还是一个标准SQL。Flex这个工具生成读入后缀是l的词法文件，然后输出一个lex.yy.c的文件，我写了个程序测试这个lex.yy.c。我的目标是把这个解析器做成MFC DLL或者能输出xml的标准程序，这样以后的元数据项目就能直接用了，甚至能通过GUI界面处理SQL，但是Flex生成的.c文件MFC程序无法直接使用，首先要注释掉c文件中的#include &lt;unistd.h&gt;这一行，这行会报错，再修改b-&gt;yy_is_interactive = file ? (isatty( fileno(file) ) &gt; 0) : 0;这一行，让b-&gt;yy_is_interactive = 0,然后在把.c文件的的开头的#include &lt;stdio.h&gt;替换成#include &lt;stdafx.h&gt;，这样就行了么？还不行，VC2003编译还是报错，我鼓捣半天才发现，需要把lex.yy.c文件重命名成lex.yy.cpp就可以了，这一系列操作太复杂了，我写了一个批处理文件生成cpp，然后又写了一个VC的宏来修改文件，这样，按两下鼠标一切就都OK了，嘿嘿，懒人就喜欢自动化。<br>然后写了简单的界面输入SQL进行解析，解析效果不错，但是运行后却发现了两处内存泄露，一处是16386字节，一处是40字节。在这里先给非程序员普及一下内存泄露的知识，任何一个计算机程序，在运行的时候存放数据是需要内存的，需要多少内存是程序向操作系统申请的，这块内存用完了就把它还给操作系统，操作系统可以再分配给其他程序。就像我们去饭馆吃饭，饭菜就是数据，内存就是碗和碟子，我们点了菜又点汤，这时候碗不够了，我们就会喊一声：&#8220;老板，再拿两个碗来盛汤&#8221;，这就是内存申请，等我们吃完了抹嘴买单走人，服务员收拾碟子和碗，这就是内存回收，如果我们看到这家饭馆的碗太漂亮了，于是偷偷拿走一个（这事我经常干），这就是内存泄露。如果偷碗的人太多了，这家饭馆的碗就不够了，你再申请要碗老板就会说碗不够了，请稍等一下，于是你们几个人只能用一个碗吃饭，吃的很慢，这就叫内存不足。C++和C的程序太灵活了，请求碗和送回碗都是程序员自己来做，就像一个饭馆没人看管，完全靠个人自觉性来维持，因此不管是水平差也好，疏忽也好，C/C++程序会很容易产生内存泄露，java和C#就好多了，他们就相当于饭馆门口有搜身的，你一个碗也带不走。<br>这里我发现了两处内存泄露，一共16KB左右，你可能会说才16KB，现在内存都好几个GB，这么点算什么，但是如果这是一个服务器上常年不停机运行的程序，有很多人来访问，会很快把内存吃掉的。虽然我这个程序不是服务器上运行的程序，但是我能容忍程序的bug，却不能容忍内存泄露，想当年我刚刚写C++程序的时候，程序有内存泄露，我死活找不出来是哪里的问题，最后只能告诉客户说我这个程序要求内存多，你的电脑需要增加内存，于是客户增加了内存，但即使这样也不行，还需要半夜重启一下机器才可以。在此我对该客户表示深深的歉意，从此我发誓，再也不让我的程序有一个字节的内存泄露，于是深山苦练coding和调试技术，经过多年的浸淫，自己写的代码肯定不会有这样的错误了，而且别人的多复杂的问题代码我拿过来就调试，就跟饭端过来就吃一样easy。<br>这回的问题我认为很easy，调试呗，一开始以为是list有问题，这是很容易出问题的地方，CList是一个模板类，用了好多年了，好用量又足，我们一直用它，但是CList里面如果放入指针的话就要注意了，简单的Removeall是不行的，还需要一个一个的delete掉里面的对象指针，我跟踪了一遍，不是它的问题，每次内存泄露的大小都是那么多，与list的大小没关系。难道是我写的CSQLSet和CSQLNode这两个类有问题？仔细查了一遍也没问题，奇了怪了，难道是lex.yy.c不能和MFC混在一起用，我有把这个程序拆出来，用纯c做了一遍，果然没报告内存泄露，好像是问题解决了。但是我如果简单的在程序里面用MFC CString类，就会报告泄露，CString这更是久经考验的共产主义战士，不可能有问题的，太令人困惑了，后来通过艰苦的内存检查发现，其实纯c的程序也有内存泄露！只不过VC2003没有报告罢了，这简直是VC的一个大bug！这太让我失望了，以前用VC6我比较喜欢numega的调试插件，它能发现比较隐秘的bug和泄露，但是VC2003我觉得应该不错了，就没去找这样的插件，没想到啊没想到，微软还是忽悠了我一下。<br>现在问题就集中在Flex生成的lex.yy.c上了，这个程序很长，好几千行，而且作者肯定是C的高手，很多地方没看懂，太牛了，我从头到尾大概浏览一遍，里面好几处申请了内存，可能就是它们的问题，但是这程序太复杂了无法下手啊，郁闷之中上网google，输入Flex memory leak，结果发现了Adobe有一个产品也叫Flex，而且也有内存泄露问题，我倒，什么世道啊，我又加入关键字lex.yy.c，这回搜出来的对了，原来不止一个人发现了这个问题，很多人都在报告这个问题，但是讨论都没结果，找到Flex的老家sourceforge.net，打算投诉一下作者，看到上面有讨论，又搜索了一下，作者针对内存泄露的问题说了，对于制作解析非C的解析器来说，可能会有泄露问题，解决的方案是在你真的准备结束解析的时候加上这两句代码：<br>yy_delete_buffer(YY_CURRENT_BUFFER);<br>yy_init = 1;<br>我加上了，好了，困扰我两天的问题解决了，这下世界清静了。但是看到其他的帖子说Bison也会有内存泄露，前面的路还很长。 
<img src ="http://www.cppblog.com/Plator/aggbug/50981.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Plator/" target="_blank">菊馨</a> 2008-05-24 23:25 <a href="http://www.cppblog.com/Plator/archive/2008/05/24/50981.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Lex中I/O例程的解释</title><link>http://www.cppblog.com/Plator/archive/2008/05/24/50940.html</link><dc:creator>菊馨</dc:creator><author>菊馨</author><pubDate>Sat, 24 May 2008 04:05:00 GMT</pubDate><guid>http://www.cppblog.com/Plator/archive/2008/05/24/50940.html</guid><wfw:comment>http://www.cppblog.com/Plator/comments/50940.html</wfw:comment><comments>http://www.cppblog.com/Plator/archive/2008/05/24/50940.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/Plator/comments/commentRss/50940.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Plator/services/trackbacks/50940.html</trackback:ping><description><![CDATA[Lex允许直接使用I/O例程。它们是：
<ol>
    <li>
    <div align=justified><strong><em>input()</em></strong>，返回下一个输入字符； </div>
    <li>
    <div align=justified><strong><em>output(c)</em></strong>，将字符c写入输出 </div>
    <li>
    <div align=justified><strong><em>unput(c)</em></strong>，将字符c压回输入流，下次<strong><em>input()</em></strong>时被读出。 </div>
    </li>
</ol>
<p>这些例程都有默认的宏定义，但是用户可以重写它们以适应不同的需求。这些例程定义了外部文件和内部字符之间的关系，并且只能同时存在或更改。它们可以被重写使得输入或者输出被定向到特殊的位置，包括其他的程序或者内存；但是字符集的使用必须在整个例程中保持统一；<strong><em>input</em></strong>必须返回0以表示文件结束；<strong><em>unput</em></strong>和<strong><em>input</em></strong>之间的关系必须保留，否则Lex不能完成向前搜索的操作。Lex在不需要的时候不会向前搜索，但是每一个以+*?$结尾的、或者含有/的规则需要这个功能。同样，当一个表达式是另一个的前缀时，向前搜索也是必不可少的。参阅下文中有关Lex使用的字符集的讨论。默认的Lex库使用100个字符作为备用限制。 </p>
<br>（但是这些在C++中似乎存在问题，我也正在调试）
<img src ="http://www.cppblog.com/Plator/aggbug/50940.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Plator/" target="_blank">菊馨</a> 2008-05-24 12:05 <a href="http://www.cppblog.com/Plator/archive/2008/05/24/50940.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>