﻿<?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>BlogJava-蒙古狼-随笔分类-编译原理</title><link>http://www.blogjava.net/landy/category/10830.html</link><description>像狼一样凶狠</description><language>zh-cn</language><lastBuildDate>Thu, 01 Mar 2007 02:45:18 GMT</lastBuildDate><pubDate>Thu, 01 Mar 2007 02:45:18 GMT</pubDate><ttl>60</ttl><item><title>JavaCC、解析树和 XQuery 语法，第 2 部分</title><link>http://www.blogjava.net/landy/archive/2006/05/06/44767.html</link><dc:creator>独孤过客</dc:creator><author>独孤过客</author><pubDate>Sat, 06 May 2006 08:23:00 GMT</pubDate><guid>http://www.blogjava.net/landy/archive/2006/05/06/44767.html</guid><wfw:comment>http://www.blogjava.net/landy/comments/44767.html</wfw:comment><comments>http://www.blogjava.net/landy/archive/2006/05/06/44767.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/landy/comments/commentRss/44767.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/landy/services/trackbacks/44767.html</trackback:ping><description><![CDATA[
		<blockquote>本文的第 1 部分简要讨论了语法、解析器和 BNF。然后它介绍了 JavaCC，一个流行的解析器生成器。第 2 部分演示了如何修改第 1 部分中的样本代码，这样就可以使用附加工具 JJTree 来构建相同解析的解析树表示。您将探索这种方法的优点，并研究如何编写 Java 代码在运行时遍历该解析树以便恢复其状态信息，并对正在解析的表达式求值。本文结尾将演示如何开发通用例程，用于遍历从一小部分 XQuery 语法生成的解析树，并对其求值。</blockquote>
		<p>使用 JavaCC 解析器生成器有一个严重缺点：许多或大多数客户机端 Java 代码需要嵌入到 .jj 语法脚本中，该脚本编码了您的 BNF（巴科斯-诺尔范式，Backus-Naur Form）。这意味着您失去了在开发周期中合适的 Java IDE 可以向您提供的许多优点。</p>
		<p>开始使用 JJTree 吧，它是 JavaCC 的伙伴工具。JJTree 被设置成提供一个解析器，该解析器在运行时的主要工作不是执行嵌入的 Java 操作，而是构建正在解析的表达式的独立解析树表示。这样，您就可以独立于生成该解析树的解析代码，捕捉在运行时易于遍历和查询的单个树中的解析会话的状态。使用解析树表示还会使调试变得更容易，并缩短开发时间。JJTree 是作为 JavaCC 分发版（distribution）的一部分发布的（请参阅 <a href="http://www-128.ibm.com/developerworks/cn/xml/x-javacc/part2/#resources"><font color="#996699">参考资料</font></a>）。 </p>
		<p>在我们继续之前，我要特别提一下，术语 <b>解析树</b>和 <b>抽象语法树</b>（或 AST）描述了非常相似的语法结构。严格地讲，对于我在下面提到的解析树，语言理论家更精确地把它称作 AST。 </p>
		<p>要使用 JJTree，您需要能够：</p>
		<ol>
				<li>创建 JJTree 作为输入获取的 .jjt 脚本 
</li>
				<li>编写客户机端代码以遍历在运行时生成的解析树并对其求值 </li>
		</ol>
		<p>本文演示了如何执行这两种操作。它并没有涵盖所有内容，但肯定能带您入门。</p>
		<p>
				<a name="1">
						<span class="atitle">
								<strong>
										<font size="4">JJTree 基础知识</font>
								</strong>
						</span>
				</a>
		</p>
		<p>JJTree 是一个预处理器，为特定 BNF 生成解析器只需要简单的两步：</p>
		<ol>
				<li>对所谓的 .jjt 文件运行 JJTree；它会产生一个中间的 .jj 文件 
</li>
				<li>用 JavaCC 编译该文件（ <a href="http://www.ibm.com/developerworks/xml/library/x-javacc1.html"><font color="#5c81a7">第 1 部分</font></a>中讨论了这个过程） </li>
		</ol>
		<p>幸运的是，.jjt 文件的结构只是我在第 1 部分中向您显示的 .jj 格式的较小扩展。主要区别是 JJTree 添加了一个新的语法 <b>node-constructor</b>构造，该构造可以让您指定在解析期间在哪里以及在什么条件下生成解析树节点。换句话说，该构造管理由解析器构造的解析树的形状和内容。 </p>
		<p>清单 1 显示了一个简单的 JavaCC .jj 脚本，它类似于您在第 1 部分中看到的脚本。为简便起见，我只显示了结果。</p>
		<br />
		<a name="code1">
				<b>清单 1. simpleLang 的 JavaCC 语法</b>
		</a>
		<br />
		<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
				<tbody>
						<tr>
								<td>
										<pre>
												<code class="section">
														<font face="Lucida Console">void simpleLang()   : {}    { addExpr() &lt;EOF&gt; }

void addExpr()      : {}    { integerLiteral() ( "+" integerLiteral() )? }

void integerLiteral()   : {}    { &lt;INT&gt; }

SKIP  : { " " | "\t" | "\n" | "\r" }

TOKEN : { &lt; INT : ( ["0" - "9"] )+ &gt; }

</font>
												</code>
										</pre>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>该语法说明了该语言中的合法表达式包含：</p>
		<ol>
				<li>单个整数文字，或 
</li>
				<li>一个整数文字，后面跟一个加号，再跟另一个整数文字。 </li>
		</ol>
		<p>对应的 JJTree .jjt 脚本（再次声明，略有简化）看上去可能如下：</p>
		<br />
		<a name="code2">
				<b>清单 2. 等价于清单 1 中的 JavaCC 语法的 JJTree</b>
		</a>
		<br />
		<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
				<tbody>
						<tr>
								<td>
										<pre>
												<code class="section">
														<font face="Lucida Console">SimpleNode simpleLang() : #Root       {}  { addExpr() &lt;EOF&gt; { return jjtThis; }}

void addExpr()          :             {}  { integerLiteral()

                                          ( "+" integerLiteral() #Add(2) )? }

void integerLiteral()   : #IntLiteral {}  { &lt;INT&gt; }

SKIP  : { " " | "\t" | "\n" | "\r" }

TOKEN : { &lt; INT : ( ["0" - "9"] )+ &gt; }

</font>
												</code>
										</pre>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>该脚本对您已经看到的脚本添加了一些新的语法特性。现在，我们只讨论突出显示的部分。以后，我会详细说明。</p>
		<br />
		<table cellspacing="0" cellpadding="0" width="100%" border="0">
				<tbody>
						<tr>
								<td>
										<img style="WIDTH: 500px; CURSOR: pointer" onclick="javascript:window.open(this.src);" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" onload="javascript:if(this.width&gt;500){this.resized=true;this.style.width=500;}" resized="true" />
								</td>
						</tr>
				</tbody>
		</table>
		<table class="no-print" cellspacing="0" cellpadding="0" align="right">
				<tbody>
						<tr align="right">
								<td>
										<table cellspacing="0" cellpadding="0" border="0">
												<tbody>
														<tr>
																<td valign="center">
																		<img style="CURSOR: pointer" onclick="javascript:window.open(this.src);" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" onload="javascript:if(this.width&gt;500){this.resized=true;this.style.width=500;}" border="0" />
																		<br />
																</td>
																<td valign="top" align="right">
																		<a class="fbox" href="http://www-128.ibm.com/developerworks/cn/xml/x-javacc/part2/#main">
																				<b>
																						<font face="Verdana" color="#996699">回页首</font>
																				</b>
																		</a>
																</td>
														</tr>
												</tbody>
										</table>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<br />
		<p>
				<a name="2">
						<span class="atitle">
								<strong>
										<font size="4">逐句说明 JJTree 语法</font>
								</strong>
						</span>
				</a>
		</p>
		<p>首先请注意，最顶层的 <code><font face="Courier" size="1">simpleLang()</font></code> 结果的 JavaCC 的过程性语法现在指定了一个返回类型： <code><font face="Courier" size="1">SimpleNode</font></code> 。它与嵌入的 Java 操作 <code><font face="Courier" size="1">return jjtThis</font></code> （有一点为 JJTree 虚张声势）一起指定了从应用程序代码调用解析器的 <code><font face="Courier" size="1">simpleLang()</font></code> 方法将返回解析树的根，然后这个根将用于树遍历。 </p>
		<p>在 JavaCC 中，解析器调用看上去如下：</p>
		<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
				<tbody>
						<tr>
								<td>
										<pre>
												<code class="section">
														<font face="Lucida Console">   SimpleParser parser = new SimpleParser(new StringReader( expression ));

    parser.simpleLang();

</font>
												</code>
										</pre>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>而现在看上去象下面这样：</p>
		<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
				<tbody>
						<tr>
								<td>
										<pre>
												<code class="section">
														<font face="Lucida Console">   SimpleParser parser = new SimpleParser(new StringReader( expression ));

    SimpleNode rootNode = parser.simpleLang();

</font>
												</code>
										</pre>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>注：所抓取的根节点并不仅仅是 <code><font face="Courier" size="1">SimpleNode</font></code> 类型。它其实是 <code><font face="Courier" size="1">Root</font></code> 类型，正如 <a href="http://www-128.ibm.com/developerworks/cn/xml/x-javacc/part2/#code2"><font color="#996699">清单 2</font></a> 中的 <code><font face="Courier" size="1">#Root</font></code> 伪指令所指定的（虽然您不会在上述调用代码中那样使用）。 <code><font face="Courier" size="1">Root</font></code> 是 <code><font face="Courier" size="1">SimpleNode</font></code> 子代，就象 JJTree 生成的解析器构造的每个节点一样。我将在下面向您显示 <code><font face="Courier" size="1">SimpleNode</font></code> 的一些内置方法。 </p>
		<p>
				<code>
						<font face="Courier" size="1">addExpr()</font>
				</code> 结果中的 <code><font face="Courier" size="1">#Add(2)</font></code> 构造与上述的 <code><font face="Courier" size="1">#Root</font></code> 伪指令不同，体现在以下几方面； </p>
		<ul>
				<li>它是参数化的。树构建器在构造树期间使用节点堆栈；没有参数的节点构建器的缺省行为是将自己放在正在构造的解析树的顶部，将所有节点弹出在同一个 <i>节点作用域</i> 中创建的节点堆栈，并把自己提升到那些节点父代的位置。参数 <code><font face="Courier" size="1">2</font></code> 告诉新的父节点（在此示例中是一个 <code><font face="Courier" size="1">Add</font></code> 节点）要恰好采用 <i>两个</i>子节点，它们是 <a href="http://www-128.ibm.com/developerworks/cn/xml/x-javacc/part2/#bullet2"><font color="#996699">下一段文字</font></a> 中描述的两个 <code><font face="Courier" size="1">IntLiteral</font></code> 子节点。JJTree 文档更详细地描述了这个过程。使用好的调试器在运行时遍历解析树是另一个宝贵的辅助方法，它有助于理解树构建在 JJTree 中是如何工作的。 
</li>
				<li>
						<a name="bullet2">将 <code><font face="Courier" size="1">#Root</font></code> 伪指令放在其结果的主体之外表示 <i>每次</i> 遍历该结果时都会生成一个 <code><font face="Courier" size="1">Root</font></code> 节点（而在此特定示例中，只允许发生一次），这一点具有同等的重要性 </a>。但是，将 <code><font face="Courier" size="1">#Add(2)</font></code> 伪指令放在可选的“零或一”项中表示仅当在解析期间遍历包含它的选择子句时 ― 换句话说，当该结果表示一个真的加法操作时 ― 才 <i>有条件地</i> 生成一个 <code><font face="Courier" size="1">Add</font></code> 节点。当发生这种情况时，会遍历 <code><font face="Courier" size="1">integerLiteral()</font></code> 两次，每次调用时都将一个 <code><font face="Courier" size="1">IntLiteral</font></code> 节点添加到树上。这两个 <code><font face="Courier" size="1">IntLiteral</font></code> 节点都成为调用它们的 <code><font face="Courier" size="1">Add</font></code> 节点的子节点。但是，如果正在解析的表达式是单个整数，那么作为结果的 <code><font face="Courier" size="1">IntLiteral</font></code> 节点将直接成为 <code><font face="Courier" size="1">Root</font></code> 的一个子节点。 </li>
		</ul>
		<p>一图胜千言（引用一句古老的谚语）。以下是由上述语法生成的两种类型的解析树的图形表示：</p>
		<br />
		<a name="Figure1">
				<b>图 1：单个整数表达式的解析树 </b>
		</a>
		<br />
		<img style="CURSOR: pointer" onclick="javascript:window.open(this.src);" src="http://www-128.ibm.com/developerworks/cn/xml/x-javacc/part2/fig1.gif" width="80" onload="javascript:if(this.width&gt;500){this.resized=true;this.style.width=500;}" />
		<br />
		<br />
		<a name="Figure2">
				<b>图 2：加法操作的解析树 </b>
		</a>
		<br />
		<img style="CURSOR: pointer" onclick="javascript:window.open(this.src);" src="http://www-128.ibm.com/developerworks/cn/xml/x-javacc/part2/fig2.gif" width="137" onload="javascript:if(this.width&gt;500){this.resized=true;this.style.width=500;}" />
		<br />
		<p>让我们更详细地研究 <code><font face="Courier" size="1">SimpleNode</font></code> 的类层次结构。 </p>
		<br />
		<table cellspacing="0" cellpadding="0" width="100%" border="0">
				<tbody>
						<tr>
								<td>
										<img style="WIDTH: 500px; CURSOR: pointer" onclick="javascript:window.open(this.src);" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" onload="javascript:if(this.width&gt;500){this.resized=true;this.style.width=500;}" resized="true" />
								</td>
						</tr>
				</tbody>
		</table>
		<table class="no-print" cellspacing="0" cellpadding="0" align="right">
				<tbody>
						<tr align="right">
								<td>
										<table cellspacing="0" cellpadding="0" border="0">
												<tbody>
														<tr>
																<td valign="center">
																		<img style="CURSOR: pointer" onclick="javascript:window.open(this.src);" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" onload="javascript:if(this.width&gt;500){this.resized=true;this.style.width=500;}" border="0" />
																		<br />
																</td>
																<td valign="top" align="right">
																		<a class="fbox" href="http://www-128.ibm.com/developerworks/cn/xml/x-javacc/part2/#main">
																				<b>
																						<font face="Verdana" color="#996699">回页首</font>
																				</b>
																		</a>
																</td>
														</tr>
												</tbody>
										</table>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<br />
		<p>
				<a name="3">
						<span class="atitle">
								<strong>
										<font size="4">使用解析树</font>
								</strong>
						</span>
				</a>
		</p>
		<p>在 .jjt 脚本中声明的每个节点都指示解析器生成 JJTree <code><font face="Courier" size="1">SimpleNode</font></code> 的一个子类。接下来， <code><font face="Courier" size="1">SimpleNode</font></code> 又实现名为 <code><font face="Courier" size="1">Node</font></code> 的 Java 接口。这两个类的源文件都是由 JJTree 脚本和定制 .jj 文件一起自动生成的。 <a href="http://www-128.ibm.com/developerworks/cn/xml/x-javacc/part2/#code1"><font color="#996699">清单 1</font></a> 显示了定制 .jj 文件的当前示例。在当前示例中，JJTree 还提供了您自己的 <code><font face="Courier" size="1">Root</font></code> 、 <code><font face="Courier" size="1">Add</font></code> 和 <code><font face="Courier" size="1">IntLiteral</font></code> 类以及没有在这里看到的一些附加的助手类的源文件。 </p>
		<p>所有 <code><font face="Courier" size="1">SimpleNode</font></code> 子类都继承了有用的行为。 <code><font face="Courier" size="1">SimpleNode</font></code> 方法 <code><font face="Courier" size="1">dump()</font></code> 就是这样一个例子。它还充当了我以前的论点（使用解析树使调试更容易，从而缩短开发时间）的示例。以下三行客户机端代码的代码片段实例化了解析器、调用解析器、抓取所返回的解析树，并且将一个简单的解析树的文本表示转储到控制台： </p>
		<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
				<tbody>
						<tr>
								<td>
										<pre>
												<code class="section">
														<font face="Lucida Console">   SimpleParser parser = new SimpleParser(new StringReader( expression ));

    SimpleNode rootNode = parser.simpleLang();

    rootNode.dump();

</font>
												</code>
										</pre>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>
				<a href="http://www-128.ibm.com/developerworks/cn/xml/x-javacc/part2/#Figure2">
						<font color="#996699">图 2</font>
				</a>中的树的调试输出是： </p>
		<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
				<tbody>
						<tr>
								<td>
										<pre>
												<code class="section">
														<font face="Lucida Console">   Root

        Add

            IntLiteral

            IntLiteral

</font>
												</code>
										</pre>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<br />
		<table cellspacing="0" cellpadding="0" width="100%" border="0">
				<tbody>
						<tr>
								<td>
										<font face="Lucida Console">
												<img style="WIDTH: 500px; CURSOR: pointer" onclick="javascript:window.open(this.src);" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" onload="javascript:if(this.width&gt;500){this.resized=true;this.style.width=500;}" resized="true" />
										</font>
								</td>
						</tr>
				</tbody>
		</table>
		<table class="no-print" cellspacing="0" cellpadding="0" align="right">
				<tbody>
						<tr align="right">
								<td>
										<table cellspacing="0" cellpadding="0" border="0">
												<tbody>
														<tr>
																<td valign="center">
																		<font face="Lucida Console">
																				<img style="CURSOR: pointer" onclick="javascript:window.open(this.src);" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" onload="javascript:if(this.width&gt;500){this.resized=true;this.style.width=500;}" border="0" />
																				<br />
																		</font>
																</td>
																<td valign="top" align="right">
																		<a class="fbox" href="http://www-128.ibm.com/developerworks/cn/xml/x-javacc/part2/#main">
																				<b>
																						<font face="Verdana" color="#996699">回页首</font>
																				</b>
																		</a>
																</td>
														</tr>
												</tbody>
										</table>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<br />
		<p>
				<a name="4">
						<span class="atitle">
								<strong>
										<font size="4">辅助导航</font>
								</strong>
						</span>
				</a>
		</p>
		<p>另一个有用的内置 <code><font face="Courier" size="1">SimpleNode</font></code> 方法是 <code><font face="Courier" size="1">jjtGetChild(int)</font></code> 。当您在客户机端向下浏览解析树，并且遇到 <code><font face="Courier" size="1">Add</font></code> 节点时，您会要抓取它的 <code><font face="Courier" size="1">IntLiteral</font></code> 子节点、抽取它们表示的整数值，并将这些数字加到一起 ― 毕竟，那是用来练习的。假设下一段代码中显示的 <code><font face="Courier" size="1">addNode</font></code> 是表示我们感兴趣的 <code><font face="Courier" size="1">Add</font></code> 类型节点的变量，那我们就可以访问 <code><font face="Courier" size="1">addNode</font></code> 的两个子节点。（ <code><font face="Courier" size="1">lhs</font></code> 和 <code><font face="Courier" size="1">rhs</font></code> 分别是 <i>左边（left-hand side）</i>和 <i>右边（right-hand side）</i>的常用缩写。） </p>
		<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
				<tbody>
						<tr>
								<td>
										<pre>
												<code class="section">
														<font face="Lucida Console">    SimpleNode lhs = addNode.jjtGetChild( 0 );

     SimpleNode rhs = addNode.jjtGetChild( 1 );

</font>
												</code>
										</pre>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>即使到目前为止您已经执行了所有操作，但您仍然没有足够的信息来计算该解析树表示的算术运算的结果。您的当前脚本已经省略了一个重要的细节：树中的两个 <code><font face="Courier" size="1">IntLiteral</font></code> 节点实际上不包含它们声称要表示的整数。那是因为当记号赋予器（tokenizer）在输入流中遇到它们时，您没有将它们的值保存到树中；您需要修改 <code><font face="Courier" size="1">integerLiteral()</font></code> 结果来执行该操作。您还需要将一些简单的存取器方法添加到 <code><font face="Courier" size="1">SimpleNode</font></code> 。 </p>
		<br />
		<table cellspacing="0" cellpadding="0" width="100%" border="0">
				<tbody>
						<tr>
								<td>
										<img style="WIDTH: 500px; CURSOR: pointer" onclick="javascript:window.open(this.src);" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" onload="javascript:if(this.width&gt;500){this.resized=true;this.style.width=500;}" resized="true" />
								</td>
						</tr>
				</tbody>
		</table>
		<table class="no-print" cellspacing="0" cellpadding="0" align="right">
				<tbody>
						<tr align="right">
								<td>
										<table cellspacing="0" cellpadding="0" border="0">
												<tbody>
														<tr>
																<td valign="center">
																		<img style="CURSOR: pointer" onclick="javascript:window.open(this.src);" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" onload="javascript:if(this.width&gt;500){this.resized=true;this.style.width=500;}" border="0" />
																		<br />
																</td>
																<td valign="top" align="right">
																		<a class="fbox" href="http://www-128.ibm.com/developerworks/cn/xml/x-javacc/part2/#main">
																				<b>
																						<font face="Verdana" color="#996699">回页首</font>
																				</b>
																		</a>
																</td>
														</tr>
												</tbody>
										</table>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<br />
		<p>
				<a name="5">
						<span class="atitle">
								<strong>
										<font size="4">保存和恢复状态</font>
								</strong>
						</span>
				</a>
		</p>
		<p>要将所扫描的记号的值存储到适当的节点中，将以下修改添加到 <code><font face="Courier" size="1">SimpleNode</font></code> ： </p>
		<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
				<tbody>
						<tr>
								<td>
										<pre>
												<code class="section">
														<font face="Lucida Console">   public class SimpleNode extends Node

    {

        String m_text;

        public void   setText( String text ) { m_text = text; }

        public String getText()              { return m_text; }

        ...

    }

</font>
												</code>
										</pre>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>将 JJTree 脚本中的以下结果：</p>
		<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
				<tbody>
						<tr>
								<td>
										<pre>
												<code class="section">
														<font face="Lucida Console">   void integerLiteral() : #IntLiteral {} &lt;INT&gt; }

</font>
												</code>
										</pre>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>更改成：</p>
		<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
				<tbody>
						<tr>
								<td>
										<pre>
												<code class="section">
														<font face="Lucida Console">   void integerLiteral() : #IntLiteral { Token t; }

                                        { t=&lt;INT&gt; { jjtThis.setText( t.image );} }

</font>
												</code>
										</pre>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>该结果抓取它刚在 <code><font face="Courier" size="1">t.image</font></code> 中遇到的整数的原始文本值，并使用您的 <code><font face="Courier" size="1">setText()</font></code> setter 方法将该字符串存储到当前节点中。 <a href="http://www-128.ibm.com/developerworks/cn/xml/x-javacc/part2/#code5"><font color="#996699">清单 5</font></a> 中的客户机端 <code><font face="Courier" size="1">eval()</font></code> 代码显示了如何使用相应的 <code><font face="Courier" size="1">getText()</font></code> getter 方法。 </p>
		<p>可以很容易地修改 <code><font face="Courier" size="1">SimpleNode.dump()</font></code> ，以提供任何节点的 <code><font face="Courier" size="1">m_text</font></code> 值供其在解析期间存储 ― 我把这作为一个众所周知的练习留给您来完成。这将让您更形象地理解在进行调试时解析树看起来是什么样子。例如，如果您解析了“42 + 1”，略经修改的 <code><font face="Courier" size="1">dump()</font></code> 例程可以生成以下有用的输出： </p>
		<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
				<tbody>
						<tr>
								<td>
										<pre>
												<code class="section">
														<font face="Lucida Console">   Root

        Add

            IntLiteral[42]

            IntLiteral[1]

</font>
												</code>
										</pre>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<br />
		<table cellspacing="0" cellpadding="0" width="100%" border="0">
				<tbody>
						<tr>
								<td>
										<font face="Lucida Console">
												<img style="WIDTH: 500px; CURSOR: pointer" onclick="javascript:window.open(this.src);" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" onload="javascript:if(this.width&gt;500){this.resized=true;this.style.width=500;}" resized="true" />
										</font>
								</td>
						</tr>
				</tbody>
		</table>
		<table class="no-print" cellspacing="0" cellpadding="0" align="right">
				<tbody>
						<tr align="right">
								<td>
										<table cellspacing="0" cellpadding="0" border="0">
												<tbody>
														<tr>
																<td valign="center">
																		<font face="Lucida Console">
																				<img style="CURSOR: pointer" onclick="javascript:window.open(this.src);" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" onload="javascript:if(this.width&gt;500){this.resized=true;this.style.width=500;}" border="0" />
																				<br />
																		</font>
																</td>
																<td valign="top" align="right">
																		<a class="fbox" href="http://www-128.ibm.com/developerworks/cn/xml/x-javacc/part2/#main">
																				<b>
																						<font face="Verdana" color="#996699">回页首</font>
																				</b>
																		</a>
																</td>
														</tr>
												</tbody>
										</table>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<br />
		<p>
				<a name="6">
						<span class="atitle">
								<strong>
										<font size="4">组合：XQuery 的 BNF 代码片段</font>
								</strong>
						</span>
				</a>
		</p>
		<p>让我们通过研究实际语法的一个代码片段来进行组合和总结。我将向您演示一段非常小的 XQuery 的 BNF 子集，这是 XML 的查询语言的 W3C 规范（请参阅 <a href="http://www-128.ibm.com/developerworks/cn/xml/x-javacc/part2/#resources"><font color="#996699">参考资料</font></a>）。我在这里所说的大多数内容也适用于 XPath，因为这两者共享了许多相同的语法。我还将简要地研究运算符优先级的问题，并将树遍历代码推广到成熟的递归例程中，该例程可以处理任意复杂的解析树。 </p>
		<p>
				<a href="http://www-128.ibm.com/developerworks/cn/xml/x-javacc/part2/#code3">
						<font color="#996699">清单 3</font>
				</a>显示了您要使用的 XQuery 语法片段。这段 BNF 摘自 2002 年 11 月 15 日的工作草案： </p>
		<br />
		<a name="code3">
				<b>清单 3：一部分 XQuery 语法</b>
		</a>
		<br />
		<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
				<tbody>
						<tr>
								<td>
										<pre>
												<code class="section">
														<font face="Lucida Console">[21]  Query              ::= QueryProlog QueryBody

    ...

[23]  QueryBody          ::= ExprSequence?

[24]  ExprSequence       ::= Expr ( "," Expr )*

[25]  Expr               ::= OrExpr

    ...

[35]  RangeExpr          ::= AdditiveExpr ( "to"  AdditiveExpr )*

[36]  AdditiveExpr       ::= MultiplicativeExpr (("+" | "-") MultiplicativeExpr )*

[37]  MultiplicativeExpr ::= UnionExpr (("*" | "div" | "idiv" | "mod") UnaryExpr )*

      ...

</font>
												</code>
										</pre>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>您将要构建一个刚好适合的 JJTree 语法脚本来处理结果 [36] 和 [37] 中的 <code><font face="Courier" size="1">+</font></code> 、 <code><font face="Courier" size="1">-</font></code> 、 <code><font face="Courier" size="1">*</font></code> 和 <code><font face="Courier" size="1">div</font></code> 运算符，而且简单地假设该语法所知道的唯一数据类型是整数。该样本语法 <i>非常</i>小，并不能妥善处理 XQuery 支持的丰富的表达式和数据类型。但是，如果您要为更大、更复杂的语法构建解析器，它应该能给您使用 JavaCC 和 JJTree 的入门知识。 </p>
		<p>
				<a href="http://www-128.ibm.com/developerworks/cn/xml/x-javacc/part2/#code4">
						<font color="#996699">清单 4</font>
				</a> 显示了 .jjt 脚本。请注意该文件顶部的 <code><font face="Courier" size="1">options{}</font></code> 块。这些选项（还有许多其它可用选项开关）指定了其中树构建在本例中是以 <i>多重</i> 方式运行的，即节点构造器用于显式地命名所生成节点的类型。备用方法（不在这里研究）是结果只将 <code><font face="Courier" size="1">SimpleNode</font></code> 节点提供给解析树，而不是它的子类。如果您想要避免扩散节点类，那么该选项很有用。 </p>
		<p>还请注意原始的 XQuery BNF 经常将多个运算符组合到同一个结果中。在 <a href="http://www-128.ibm.com/developerworks/cn/xml/x-javacc/part2/#code4"><font color="#996699">清单 4</font></a>中，我已经将这些运算符分离到 JJTree 脚本中的单独结果中，因为这让客户机端的代码更简单。要进行组合，只需存储已扫描的运算符的值，就象对整数所进行的操作一样。 </p>
		<br />
		<a name="code4">
				<b>清单 4：清单 3 中的 XQuery 语法的 JJTree 脚本</b>
		</a>
		<br />
		<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
				<tbody>
						<tr>
								<td>
										<pre>
												<code class="section">
														<font face="Lucida Console">options {

   MULTI=true;

   NODE_DEFAULT_VOID=true;

   NODE_PREFIX="";

}

PARSER_BEGIN( XQueryParser )

package examples.example_2;

public class XQueryParser{}

PARSER_END( XQueryParser )

SimpleNode query()     #Root      : {} { additiveExpr() &lt;EOF&gt; { return jjtThis; }}

void additiveExpr()               : {} { subtractiveExpr()

                                       ( "+" subtractiveExpr() #Add(2) )* }

void subtractiveExpr()            : {} { multiplicativeExpr()

                                       ( "-" multiplicativeExpr() #Subtract(2) )* }

void multiplicativeExpr()         : {} { divExpr() ( "*" divExpr() #Mult(2) )* }

void divExpr()                    : {} { integerLiteral()

                                       ( "div" integerLiteral() #Div(2) )* }

void integerLiteral() #IntLiteral :    { Token t; }

                                       { t=&lt;INT&gt; { jjtThis.setText(t.image); }}

SKIP  : { " " | "\t" | "\n" | "\r" }

TOKEN : { &lt; INT : ( ["0" - "9"] )+ &gt; }

</font>
												</code>
										</pre>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>该 .jjt 文件引入了几个新的特性。例如，该语法中的运算结果现在是 <i>迭代的</i> ：通过使用 <code><font face="Courier" size="1">*</font></code> （零次或多次）发生指示符来表示它们的可选的第二项，这与 <a href="http://www-128.ibm.com/developerworks/cn/xml/x-javacc/part2/#code2"><font color="#996699">清单 2</font></a> 中的 <code><font face="Courier" size="1">?</font></code> （零次或一次）表示法相反。该脚本所提供的解析器可以解析任意长的表达式，如“1 + 2 * 3 div 4 + 5”。 </p>
		<p>
				<a name="N102AF">
						<span class="smalltitle">
								<strong>
										<font size="3">实现优先级</font>
								</strong>
						</span>
				</a>
		</p>
		<p>该语法还知道 <i>运算符优先级</i>。例如，您期望乘法的优先级比加法高。在实际例子中，这表示诸如“1 + 2 * 3”这样的表达式将作为“1 + ( 2 * 3 )”进行求值，而不是“( 1 + 2 ) * 3”。 </p>
		<p>优先级是通过使用级联样式实现的，其中每个结果会调用紧随其后的较高优先级的结果。级联样式和节点构造的位置和格式保证了以正确的结构生成解析树，这样树遍历可以正确执行。用一些直观图形也许更易于理解这一点。</p>
		<p>
				<a href="http://www-128.ibm.com/developerworks/cn/xml/x-javacc/part2/#Figure3">
						<font color="#996699">图 3</font>
				</a> 显示了由此语法生成的解析树，它可以让您正确地将“1 + 2 * 3”当作“1 + ( 2 * 3 )”进行求值。请注意， <code><font face="Courier" size="1">Mult</font></code> 运算符与它的项之间的联系比 <code><font face="Courier" size="1">Plus</font></code> 更紧密，而这正是您希望的： </p>
		<br />
		<a name="Figure3">
				<b>图 3. 结构正确的树 </b>
		</a>
		<br />
		<img style="CURSOR: pointer" onclick="javascript:window.open(this.src);" src="http://www-128.ibm.com/developerworks/cn/xml/x-javacc/part2/fig3.gif" width="203" onload="javascript:if(this.width&gt;500){this.resized=true;this.style.width=500;}" />
		<br />
		<p>而 <a href="http://www-128.ibm.com/developerworks/cn/xml/x-javacc/part2/#Figure4"><font color="#996699">图 4</font></a>显示的树（该语法 <i>不会</i>生成这样的树）表示您（错误地）想要将该表达式当作“(1 + 2) * 3”求值。 </p>
		<br />
		<a name="Figure4">
				<b>图 4. 结构不正确的树 </b>
		</a>
		<br />
		<img style="CURSOR: pointer" onclick="javascript:window.open(this.src);" src="http://www-128.ibm.com/developerworks/cn/xml/x-javacc/part2/fig4.gif" width="186" onload="javascript:if(this.width&gt;500){this.resized=true;this.style.width=500;}" />
		<br />
		<br />
		<table cellspacing="0" cellpadding="0" width="100%" border="0">
				<tbody>
						<tr>
								<td>
										<img style="WIDTH: 500px; CURSOR: pointer" onclick="javascript:window.open(this.src);" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" onload="javascript:if(this.width&gt;500){this.resized=true;this.style.width=500;}" resized="true" />
								</td>
						</tr>
				</tbody>
		</table>
		<table class="no-print" cellspacing="0" cellpadding="0" align="right">
				<tbody>
						<tr align="right">
								<td>
										<table cellspacing="0" cellpadding="0" border="0">
												<tbody>
														<tr>
																<td valign="center">
																		<img style="CURSOR: pointer" onclick="javascript:window.open(this.src);" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" onload="javascript:if(this.width&gt;500){this.resized=true;this.style.width=500;}" border="0" />
																		<br />
																</td>
																<td valign="top" align="right">
																		<a class="fbox" href="http://www-128.ibm.com/developerworks/cn/xml/x-javacc/part2/#main">
																				<b>
																						<font face="Verdana" color="#996699">回页首</font>
																				</b>
																		</a>
																</td>
														</tr>
												</tbody>
										</table>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<br />
		<p>
				<a name="7">
						<span class="atitle">
								<strong>
										<font size="4">遍历解析树客户机端</font>
								</strong>
						</span>
				</a>
		</p>
		<p>就象我曾答应的，我将用客户机端代码的清单作为总结，该清单将调用该解析器并遍历它生成的解析树，它使用简单而功能强大的递归 <code><font face="Courier" size="1">eval()</font></code> 函数对树遍历时遇到的每个节点执行正确操作。 <a href="http://www-128.ibm.com/developerworks/cn/xml/x-javacc/part2/#code5"><font color="#996699">清单 5</font></a>中的注释提供了关于内部 JJTree 工作的附加详细信息。 </p>
		<br />
		<a name="code5">
				<b>清单 5. 可容易泛化的 eval() 例程</b>
		</a>
		<br />
		<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
				<tbody>
						<tr>
								<td>
										<pre>
												<code class="section">
														<font face="Lucida Console">   // return the arithmetic result of evaluating 'query'

    public int parse( String query )

    //------------------------------

    {

        SimpleNode root = null;

     // instantiate the parser

        XQueryParser parser = new XQueryParser( new StringReader( query ));

            try {
            // invoke it via its topmost production

            // and get a parse tree back

            root = parser.query();

            root.dump("");

        }

        catch( ParseException pe ) {

            System.out.println( "parse(): an invalid expression!" );

        }

        catch( TokenMgrError e )  {

            System.out.println( "a Token Manager error!" );

        }

        // the topmost root node is just a placeholder; ignore it.

        return eval( (SimpleNode) root.jjtGetChild(0) );

    }

    int eval( SimpleNode node )

    //-------------------------

    {

        // each node contains an id field identifying its type.

        // we switch on these. we could use instanceof, but that's less efficient

        // enum values such as JJTINTLITERAL come from the interface file

        // SimpleParserTreeConstants, which SimpleParser implements.

        // This interface file is one of several auxilliary Java sources

        // generated by JJTree. JavaCC contributes several others.

        int id = node.id;

        // eventually the buck stops here and we unwind the stack

        if ( node.id == JJTINTLITERAL )

            return Integer.parseInt( node.getText() );

        SimpleNode lhs =  (SimpleNode) node.jjtGetChild(0);

        SimpleNode rhs =  (SimpleNode) node.jjtGetChild(1);

        switch( id )

        {

            case JJTADD :       return eval( lhs ) + eval( rhs );

            case JJTSUBTRACT :  return eval( lhs ) - eval( rhs );

            case JJTMULT :      return eval( lhs ) * eval( rhs );

            case JJTDIV :       return eval( lhs ) / eval( rhs );

            default :

                throw new java.lang.IllegalArgumentException(

                                  "eval(): invalid operator!" );

        }

    }

</font>
												</code>
										</pre>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<br />
		<table cellspacing="0" cellpadding="0" width="100%" border="0">
				<tbody>
						<tr>
								<td>
										<font face="Lucida Console">
												<img style="WIDTH: 500px; CURSOR: pointer" onclick="javascript:window.open(this.src);" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" onload="javascript:if(this.width&gt;500){this.resized=true;this.style.width=500;}" resized="true" />
										</font>
								</td>
						</tr>
				</tbody>
		</table>
		<table class="no-print" cellspacing="0" cellpadding="0" align="right">
				<tbody>
						<tr align="right">
								<td>
										<table cellspacing="0" cellpadding="0" border="0">
												<tbody>
														<tr>
																<td valign="center">
																		<font face="Lucida Console">
																				<img style="CURSOR: pointer" onclick="javascript:window.open(this.src);" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" onload="javascript:if(this.width&gt;500){this.resized=true;this.style.width=500;}" border="0" />
																				<br />
																		</font>
																</td>
																<td valign="top" align="right">
																		<a class="fbox" href="http://www-128.ibm.com/developerworks/cn/xml/x-javacc/part2/#main">
																				<b>
																						<font face="Verdana" color="#996699">回页首</font>
																				</b>
																		</a>
																</td>
														</tr>
												</tbody>
										</table>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<br />
		<p>
				<a name="8">
						<span class="atitle">
								<strong>
										<font size="4">结束语</font>
								</strong>
						</span>
				</a>
		</p>
		<p>如果您想要查看可以处理许多实际 XQuery 语法的功能更丰富的 <code><font face="Courier" size="1">eval()</font></code> 函数版本，欢迎下载我的开放源码 XQuery 实现（XQEngine）的副本（请参阅 <a href="http://www-128.ibm.com/developerworks/cn/xml/x-javacc/part2/#resources"><font color="#996699">参考资料</font></a> ）。它的 <code><font face="Courier" size="1">TreeWalker.eval()</font></code> 例程 <i>例举</i>了 30 多种 XQuery 节点类型。还提供了一个 .jjt 脚本。 </p>
		<br />
		<table cellspacing="0" cellpadding="0" width="100%" border="0">
				<tbody>
						<tr>
								<td>
										<img style="WIDTH: 500px; CURSOR: pointer" onclick="javascript:window.open(this.src);" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" onload="javascript:if(this.width&gt;500){this.resized=true;this.style.width=500;}" resized="true" />
								</td>
						</tr>
				</tbody>
		</table>
		<table class="no-print" cellspacing="0" cellpadding="0" align="right">
				<tbody>
						<tr align="right">
								<td>
										<table cellspacing="0" cellpadding="0" border="0">
												<tbody>
														<tr>
																<td valign="center">
																		<img style="CURSOR: pointer" onclick="javascript:window.open(this.src);" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" onload="javascript:if(this.width&gt;500){this.resized=true;this.style.width=500;}" border="0" />
																		<br />
																</td>
																<td valign="top" align="right">
																		<a class="fbox" href="http://www-128.ibm.com/developerworks/cn/xml/x-javacc/part2/#main">
																				<b>
																						<font face="Verdana" color="#996699">回页首</font>
																				</b>
																		</a>
																</td>
														</tr>
												</tbody>
										</table>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<br />
		<p>
				<a name="resources">
						<span class="atitle">
								<strong>
										<font size="4">参考资料 </font>
								</strong>
						</span>
				</a>
		</p>
		<ul>
				<li>您可以参阅本文在 developerWorks 全球站点上的 <a href="http://www.ibm.com/developerworks/library/x-javacc2/index.html"><font color="#5c81a7">英文原文</font></a>. <br /><br /></li>
				<li>请加入本文的 <a href="javascript:void forumWindow()"><font color="#5c81a7">论坛</font></a>。（您也可以单击本文顶部或底部的 <b>讨论</b>来访问论坛。） <br /><br /></li>
				<li>有关语法、解析器和 BNF 的简要讨论和 JavaCC 的介绍，请回顾本文的 <a href="http://www.ibm.com/developerworks/xml/library/x-javacc1.html"><font color="#5c81a7">第 1 部分</font></a>。您还会找到使用 JavaCC 构建定制解析器的样本代码，它从语法的 BNF 描述开始。 <br /><br /></li>
				<li>请访问免费（但不是开放源码） <a href="http://www.webgain.com/products/java_cc/"><font color="#5c81a7">JavaCC 和 JJTree 的分发版</font></a>。 <br /><br /></li>
				<li>请在 <a href="http://www.w3.org/XML/Query"><font color="#5c81a7">XML Query 主页</font></a>上寻找关于 W3C 的 XQuery 和 XPath 规范的更多信息。 <br /><br /></li>
				<li>
						<a href="http://www.fatdog.com/">
								<font color="#5c81a7">XQEngine</font>
						</a>是作者编写的 XQuery 引擎的基于 Java 的开放源码实现。 <br /><br /></li>
				<li>想要了解 BNF 的更多知识吗？请访问 <a href="http://www.wikipedia.org/wiki/Backus-Naur_form"><font color="#5c81a7">Wikipedia.org</font></a>。 <br /><br /></li>
				<li>请在 developerWorks <a href="http://www.ibm.com/developerworks/xml/"><font color="#5c81a7">XML</font></a>和 <a href="http://www.ibm.com/developerworks/java/"><font color="#5c81a7">Java 技术</font></a>专区中寻找本文中所涵盖技术的更多信息。 <br /><br /></li>
				<li>
						<a href="http://www-3.ibm.com/software/info1/websphere/index.jsp?tab=landings/studiosplashv5">
								<font color="#5c81a7">IBM WebSphere Studio</font>
						</a>提供了以 Java 和其它语言自动进行 XML 开发的一组工具。它与 <a href="http://www-3.ibm.com/software/webservers/appserv/"><font color="#5c81a7">WebSphere Application Server</font></a>紧密集成，而且还可以用于其它 J2EE 服务器。 <br /><br /></li>
				<li>了解您怎样可以成为一名 <a href="http://www-1.ibm.com/certify/certs/adcdxmlrt.shtml&amp;origin=x"><font color="#5c81a7">IBM 认证的 XML 及相关技术开发人员</font></a>。 <br /></li>
		</ul>
		<br />
		<table cellspacing="0" cellpadding="0" width="100%" border="0">
				<tbody>
						<tr>
								<td>
										<img style="WIDTH: 500px; CURSOR: pointer" onclick="javascript:window.open(this.src);" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" onload="javascript:if(this.width&gt;500){this.resized=true;this.style.width=500;}" resized="true" />
								</td>
						</tr>
				</tbody>
		</table>
		<table class="no-print" cellspacing="0" cellpadding="0" align="right">
				<tbody>
						<tr align="right">
								<td>
										<table cellspacing="0" cellpadding="0" border="0">
												<tbody>
														<tr>
																<td valign="center">
																		<img style="CURSOR: pointer" onclick="javascript:window.open(this.src);" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" onload="javascript:if(this.width&gt;500){this.resized=true;this.style.width=500;}" border="0" />
																		<br />
																</td>
																<td valign="top" align="right">
																		<a class="fbox" href="http://www-128.ibm.com/developerworks/cn/xml/x-javacc/part2/#main">
																				<b>
																						<font face="Verdana" color="#996699">回页首</font>
																				</b>
																		</a>
																</td>
														</tr>
												</tbody>
										</table>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<br />
		<p>
				<a name="author">
						<span class="atitle">
								<strong>
										<font size="4">关于作者</font>
								</strong>
						</span>
				</a>
		</p>
		<table cellspacing="0" cellpadding="0" width="100%" border="0">
				<tbody>
						<tr>
								<td colspan="2">
										<strong>
												<font size="4">
														<img style="WIDTH: 500px; CURSOR: pointer" onclick="javascript:window.open(this.src);" src="http://www.ibm.com/i/c.gif" width="100%" onload="javascript:if(this.width&gt;500){this.resized=true;this.style.width=500;}" resized="true" />
												</font>
										</strong>
								</td>
						</tr>
						<tr valign="top" align="left">
								<td>
										<p>
												<strong>
														<font size="4">
														</font>
												</strong>
										</p>
								</td>
								<td>
										<p>Howard Katz 居住在加拿大温哥华，他是 Fatdog Software 的唯一业主，该公司专门致力于开发搜索 XML 文档的软件。在过去的大约 35 年里，他一直是活跃的程序员（一直业绩良好），并且长期为计算机贸易出版机构撰写技术文章。Howard 是 Vancouver XML Developer's Association 的共同主持人，还是 Addison Wesley 即将出版的书籍 <i>The Experts on XQuery</i>的编辑，该书由 W3C 的 Query 工作组成员合著，概述了有关 XQuery 的技术前景。他和他的妻子夏天去划船，冬天去边远地区滑雪。可以通过 <a href="mailto:howardk@fatdog.com"><font color="#5c81a7">howardk@fatdog.com</font></a>与 Howard 联系。 </p>
								</td>
						</tr>
				</tbody>
		</table>
<img src ="http://www.blogjava.net/landy/aggbug/44767.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/landy/" target="_blank">独孤过客</a> 2006-05-06 16:23 <a href="http://www.blogjava.net/landy/archive/2006/05/06/44767.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>JavaCC、解析树和 XQuery 语法，第 1 部分</title><link>http://www.blogjava.net/landy/archive/2006/05/06/44766.html</link><dc:creator>独孤过客</dc:creator><author>独孤过客</author><pubDate>Sat, 06 May 2006 08:22:00 GMT</pubDate><guid>http://www.blogjava.net/landy/archive/2006/05/06/44766.html</guid><wfw:comment>http://www.blogjava.net/landy/comments/44766.html</wfw:comment><comments>http://www.blogjava.net/landy/archive/2006/05/06/44766.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/landy/comments/commentRss/44766.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/landy/services/trackbacks/44766.html</trackback:ping><description><![CDATA[
		<blockquote>在简要讨论了语法、解析器和 BNF 后，本文将介绍 JavaCC，这是一个流行的解析器生成器工具。您将开发使用 JavaCC 的样本代码来构建定制的解析器，先从语法的 BNF 描述开始。第 2 部分接着将演示如何使用辅助工具 ― JJTree 来构建同一解析的解析树表示，以及如何在运行时遍历该树，以发现其状态信息。文章将以开发构建和遍历解析树的样本代码作为结束，该解析树是您为一小部分 XQuery 语法生成的。</blockquote>
		<p>要完成最简单的日常解析任务，您不需要使用象自动化解析器生成器那样复杂的任何东西。例如，同“梳理”CSV（逗号分割的值，Comma-Separated-Value）文件的各部分同样简单的编程练习需要了解文件的结构，可能还需要了解如何使用 Java <code><font face="新宋体">StringTokenizer</font></code> 。另外，CSV 练习还需要了解很少的解析理论知识或者将自动化工具应用于任务的需求。 </p>
		<p>但是，一旦正式描述它的某种语言和语法变得复杂，那么语言中的有效表达式数量将迅速增加。而能够手工处理将任意表达式解析成其组成部分（这或多或少是解析更简明的定义）所需的代码将变得越来越困难。自动化解析器生成器减轻了这种困难。其他程序员或许也可以将您生成的解析器用于他们自己的用途。</p>
		<p>
				<a name="1">
						<span class="atitle">
								<font size="4">从 BNF 开始</font>
						</span>
				</a>
		</p>
		<p>复杂语言的语法通常都是使用 BNF（巴科斯-诺尔范式，Backus-Naur Form）表示法或者其“近亲”― EBNF（扩展的 BNF）描述的。自动化工具可以使用那些描述（我将使用通用的术语 <i>BNF</i>来指代这两种变体）或与它们近似的描述来为您生成解析代码。本文就描述了这样的一种解析器-生成器工具，称为 JavaCC。我将简要地研究一下 JavaCC 的基本知识，并在结束的时候花些时间研究一下它的一个辅助工具 ― JJTree，但是在讨论中不会介绍太多的理论知识，以免偏离主题。本文力图阐明我的理念：您并不需要了解很多有关正规的解析理论就能进行解析！ </p>
		<p>为什么使用 JavaCC 呢？有几个原因：我对 XQuery 有着强烈的兴趣，而 W3C 的 XML Query 工作组恰好使用 JavaCC 来构建并测试 XQuery 语法的版本，并且构建和测试它与 XSL 组共享的 XPath 语法。我还使用 JavaCC 来提供 XQEngine 中的查询-解析代码，XQEngine 是我自己的开放源码 XQuery 实现（请参阅 <a href="http://www-128.ibm.com/developerworks/cn/xml/x-javacc/part1/#resources"><font color="#996699">参考资料</font></a>）。 </p>
		<p>最后一点（但不是最不重要的），价格是完全合适的：尽管 JavaCC 不是开放源码，但它是完全免费的。（请参阅 <a href="http://www-128.ibm.com/developerworks/cn/xml/x-javacc/part1/#resources"><font color="#996699">参考资料</font></a>以了解有关如何获得 JavaCC 的信息）。 </p>
		<br />
		<table cellspacing="0" cellpadding="0" width="100%" border="0">
				<tbody>
						<tr>
								<td>
										<img style="WIDTH: 500px; CURSOR: pointer" onclick="javascript:window.open(this.src);" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" onload="javascript:if(this.width&gt;500)this.style.width=500;" />
								</td>
						</tr>
				</tbody>
		</table>
		<table class="no-print" cellspacing="0" cellpadding="0" align="right">
				<tbody>
						<tr align="right">
								<td>
										<table cellspacing="0" cellpadding="0" border="0">
												<tbody>
														<tr>
																<td valign="center">
																		<img style="CURSOR: pointer" onclick="javascript:window.open(this.src);" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" onload="javascript:if(this.width&gt;500)this.style.width=500;" border="0" />
																		<br />
																</td>
																<td valign="top" align="right">
																		<a class="fbox" href="http://www-128.ibm.com/developerworks/cn/xml/x-javacc/part1/#main">
																				<b>
																						<font color="#996699">回页首</font>
																				</b>
																		</a>
																</td>
														</tr>
												</tbody>
										</table>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<br />
		<p>
				<a name="2">
						<span class="atitle">
								<font size="4">解析 101</font>
						</span>
				</a>
		</p>
		<p>在我开始介绍一些实际的 XQuery 语法之前，让我们先从一个非常简单的 BNF 开始，它描述了一种语言，该语言仅由两个只对整数进行运算的算术运算符构成。我称这种语言为 <code><font face="新宋体">SimpleLang</font></code> ： </p>
		<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
				<tbody>
						<tr>
								<td>
										<pre>
												<code class="section">
														<font face="Lucida Console">simpleLang     ::=   integerLiteral ( ( "+" | "-" ) integerLiteral )?
integerLiteral ::=   [ 0-9 ]+
</font>
												</code>
										</pre>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>该语法中的每个规则都是一个 <b>结果（production）</b> ，其中左边的项（结果的名称）是依据语法中的其它结果描述的。最上面的结果 <code><font face="新宋体">simpleLang</font></code> 表明，该语言中有效的（或合法的）表达式是这样构成的，一个整数值，可以任意选择其后跟一个加号（+）或减号（-）以及另一个整数值或不跟任何东西。按照这种语法，单个整数“42”是有效的，同样，表达式“42 + 1”也是有效的。第二个结果以类 regex 的方式更特定地描述了一个整数值看上去象什么：一个或多个数字的连续序列。 </p>
		<p>该语法描述了两个结果 <code><font face="新宋体">simpleLang</font></code> 和 <code><font face="新宋体">integerLiteral</font></code> 之间存在的抽象关系。它还详细描述了三个 <b>记号</b>（加号、减号和整数）组合的具体项，解析器在扫描整个输入流时希望遇到这些项。解析器中负责该任务的部件称为 <b>扫描器（scanner）</b>或 <b>记号赋予器（tokenizer）</b> 一点也不稀奇。在该语言中， <code><font face="新宋体">simpleLang</font></code> 是 <i>非终端（non-terminal）</i> 符号的一个示例，它对其它结果进行引用；另一方面，规则 <code><font face="新宋体">integerLiteral</font></code> 描述了 <i>终端（terminal）</i>符号：这是一种不能进一步分解成其它结果的符号。 </p>
		<p>如果解析器在其扫描期间发现了除这三个记号外的任何 <i>其它</i>记号，则认为它正在扫描的表达式是无效的。解析器的主要工作之一就是确定您传递给它的任何表达式的有效性，并且让您知道。一旦认为某个表达式是有效的，则它的第二项工作是将输入流分解成其组件块，并以某个有用的方式将它们提供给您。 </p>
		<br />
		<table cellspacing="0" cellpadding="0" width="100%" border="0">
				<tbody>
						<tr>
								<td>
										<img style="WIDTH: 500px; CURSOR: pointer" onclick="javascript:window.open(this.src);" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" onload="javascript:if(this.width&gt;500)this.style.width=500;" />
								</td>
						</tr>
				</tbody>
		</table>
		<table class="no-print" cellspacing="0" cellpadding="0" align="right">
				<tbody>
						<tr align="right">
								<td>
										<table cellspacing="0" cellpadding="0" border="0">
												<tbody>
														<tr>
																<td valign="center">
																		<img style="CURSOR: pointer" onclick="javascript:window.open(this.src);" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" onload="javascript:if(this.width&gt;500)this.style.width=500;" border="0" />
																		<br />
																</td>
																<td valign="top" align="right">
																		<a class="fbox" href="http://www-128.ibm.com/developerworks/cn/xml/x-javacc/part1/#main">
																				<b>
																						<font color="#996699">回页首</font>
																				</b>
																		</a>
																</td>
														</tr>
												</tbody>
										</table>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<br />
		<p>
				<a name="3">
						<span class="atitle">
								<font size="4">从 BNF 到 JavaCC</font>
						</span>
				</a>
		</p>
		<p>让我们看看如何使用 JavaCC 实现该语法。JavaCC 使用称为 .jj 的文件。该文件中的语法描述是使用非常类似于 BNF 的表示法编写的，这样从一种形式转换到另一种形式通常就相当容易。（该表示法有自己的语法，从而使其在 JavaCC 中是可表达的。）</p>
		<p>JavaCC .jj 文件语法和标准的 BNF 之间的主要区别在于：利用 JavaCC 版本，您可以在语法中嵌入操作。一旦成功遍历了语法中的那些部分，则执行这些操作。操作都是 Java 语句，它们是解析器 Java 源代码的一部分，该部分作为解析器生成过程的一部分产生。</p>
		<p>（注：除了一条 Java <code><font face="新宋体">println()</font></code> 语句外， <a href="http://www-128.ibm.com/developerworks/cn/xml/x-javacc/part1/#code1"><font color="#996699">清单 1</font></a>并不包含您需要用来对该语言中的表达式实际求值的嵌入式 Java 代码。当您研究过 JJTree 及其解析树表示后，我将对此做更详细的研究。） </p>
		<br />
		<a name="code1">
				<b>清单 1. 编码 SimpleLang 语法的完整 .jj 脚本</b>
		</a>
		<br />
		<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
				<tbody>
						<tr>
								<td>
										<pre>
												<code class="section">
														<font face="Lucida Console">PARSER_BEGIN( Parser_1 )
package examples.example_1;
public class Parser_1 {}
PARSER_END( Parser_1 )

void simpleLang() : {}             { integerLiteral() 
                                   ( ( "+" | "-" ) integerLiteral() )? &lt;EOF&gt; }
void integerLiteral() : {Token t;} { t=&lt;INT&gt; 
                                   { System.out.println("integer = "+t.image); }}

SKIP 	: { " " | "\t" | "\n" | "\r" }
TOKEN	: { &lt; INT : ( ["0" - "9"] )+ &gt; }
</font>
												</code>
										</pre>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>请注意有关该文件的下述情形：</p>
		<ul>
				<li>
						<code>
								<font face="新宋体">PARSER_BEGIN</font>
						</code> 和 <code><font face="新宋体">PARSER_END</font></code> 伪指令指定了要生成的 Java 解析器的名称（Parser_1.java），并提供一个位置以便将 Java 语句插入该类。在这个案例中，您正将一个 Java <code><font face="新宋体">package</font></code> 语句放置在文件的顶部。该 <code><font face="新宋体">package</font></code> 语句也放置在 Java 助手类文件的顶部，该文件是作为生成过程一部分产生的（请参阅 <a href="http://www-128.ibm.com/developerworks/cn/xml/x-javacc/part1/#JavaCC_process"><font color="#996699">JavaCC 编译过程</font></a> ）。尽管我在本示例中没有这样做，但是这也是一个声明实例变量的好场所，该实例变量将由您结果中的 Java 语句引用。如果您喜欢，甚至可以在这里插入 Java <code><font face="新宋体">main()</font></code> 过程，并且使用它来构建独立的应用程序，以启动和测试您正在生成的解析器。 
</li>
				<li>JavaCC 语法看上去象一个过程，而结果看上去非常象进行方法调用。这并非偶然。在 JavaCC 编译这个脚本时产生的 Java 源代码包含与这些结果具有相同名称的方法；这些方法在运行时按照它们在 .jj 脚本中调用的顺序执行。我将在 <a href="http://www-128.ibm.com/developerworks/cn/xml/x-javacc/part1/#Walking_the_code"><font color="#996699">遍历解析代码</font></a>中为您演示那是如何工作的。（这里的术语“编译”也不是偶然的 ― 解析器生成器通常也称为“编译器的编译器”。） 
</li>
				<li>花括号（{ 和 }) 内描述了结果的主体，并且排除了任何您正在嵌入的 Java 操作。请注意 <code><font face="新宋体">integerLiteral</font></code> 规则中用花括号包括的 <code><font face="新宋体">System.out.println()</font></code> 语句。该操作作为方法 <code><font face="新宋体">Parser_1.integerLiteral()</font></code> 的一部分产生。每当解析器遇到整数时，都执行该操作。 
</li>
				<li>文件结尾的 <code><font face="新宋体">SKIP</font></code> 语句表明，在记号之间可以出现空白（空格、跳格、回车和换行），空白将被忽略。 
</li>
				<li>
						<code>
								<font face="新宋体">TOKEN</font>
						</code> 语句包含类似 regex 的表达式，该表达式描述了整数记号看起来象什么。在前面的结果中，对这些记号的引用是用尖括号括起来的。 
</li>
				<li>第二个结果 <code><font face="新宋体">integerLiteral()</font></code> 声明了类型 <code><font face="新宋体">Token</font></code> （JavaCC 的内置类）的局部变量 <code><font face="新宋体">t</font></code> 。当在输入流中遇到整数时会 <i>触发</i> 该规则，该整数（象文本一样）的值被赋给实例变量 <code><font face="新宋体">t.image</font></code> 。另一个 <code><font face="新宋体">Token</font></code> 字段 <code><font face="新宋体">t.kind</font></code> 被赋值为一个枚举（enum），表明这个特殊的记号是一个整数，而不是解析器所知的另一种类型的记号。最后，在解析器中生成的 Java <code><font face="新宋体">System.out.println()</font></code> 代码可以在解析时在那个记号的内部使用 <code><font face="新宋体">t.image</font></code> 进行访问并且打印其文本值。 </li>
		</ul>
		<br />
		<table cellspacing="0" cellpadding="0" width="100%" border="0">
				<tbody>
						<tr>
								<td>
										<img style="WIDTH: 500px; CURSOR: pointer" onclick="javascript:window.open(this.src);" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" onload="javascript:if(this.width&gt;500)this.style.width=500;" />
								</td>
						</tr>
				</tbody>
		</table>
		<table class="no-print" cellspacing="0" cellpadding="0" align="right">
				<tbody>
						<tr align="right">
								<td>
										<table cellspacing="0" cellpadding="0" border="0">
												<tbody>
														<tr>
																<td valign="center">
																		<img style="CURSOR: pointer" onclick="javascript:window.open(this.src);" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" onload="javascript:if(this.width&gt;500)this.style.width=500;" border="0" />
																		<br />
																</td>
																<td valign="top" align="right">
																		<a class="fbox" href="http://www-128.ibm.com/developerworks/cn/xml/x-javacc/part1/#main">
																				<b>
																						<font color="#996699">回页首</font>
																				</b>
																		</a>
																</td>
														</tr>
												</tbody>
										</table>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<br />
		<p>
				<a name="4">
						<span class="atitle">
								<font size="4">遍历解析器代码</font>
						</span>
				</a>
		</p>
		<p>让我们非常简要地了解一下您生成的解析器的内部原理。出于下面两个原因，稍微了解由特殊的 .jj 语法生成的方法以及其在运行时的执行顺序是很有用的：</p>
		<ul>
				<li>有时候（特别是当您第一次做的时候），解析器似乎返回了不同的结果，而您认为是 .jj 文件指示它这样做的。您可以在运行时单步遍历产生的解析器代码，以便查看它到底在做什么，并相应地调整语法文件。我现在还经常这样做。 
</li>
				<li>如果您知道结果／方法的执行顺序，那么您将对在脚本中的什么地方嵌入 Java 操作以获得特定的结果有更好的理解。在第 2 部分谈论有关 JJTree 工具和解析树表示时，我将回过头来更详细地讨论这一内容。 </li>
		</ul>
		<p>尽管深入研究已生成解析器的详细信息超越了本文的范围，但是 <a href="http://www-128.ibm.com/developerworks/cn/xml/x-javacc/part1/#code2"><font color="#996699">清单 2</font></a> 还是显示了为方法 <code><font face="新宋体">Parser_1.integerLiteral()</font></code> 生成的代码。这可能会让您对最终代码看起来象什么有一些了解。特别需要注意方法中的最后一条语句： <code><font face="新宋体">System.out.println( "integer = "+t.image)</font></code> 。该语句作为嵌入 .jj 脚本的 Java 操作发挥作用。 </p>
		<br />
		<a name="code2">
				<b>清单 2. Parser_1 中生成的方法</b>
		</a>
		<br />
		<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
				<tbody>
						<tr>
								<td>
										<pre>
												<code class="section">
														<font face="Lucida Console">  static final public void integerLiteral() throws ParseException {
                            Token t;
    t = jj_consume_token(INT);
    System.out.println( "integer = "+t.image);
  }
</font>
												</code>
										</pre>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>以下高级、详尽的描述说明了这个解析器将做什么：</p>
		<ol>
				<li>最上面的方法 <code><font face="新宋体">simpleLang()</font></code> 调用 <code><font face="新宋体">integerLiteral()</font></code> 。 
</li>
				<li>
						<code>
								<font face="新宋体">integerLiteral()</font>
						</code> 希望在输入流中立即遇到一个整数，否则该表达式将无效。为了验证这一点，它调用记号赋予器（Tokenizer.java）以返回输入流中的下一个记号。记号赋予器穿过输入流，每次检查一个字符，直到它遇到一个整数或者直至文件结束。如果是前者，则以 <code><font face="新宋体">&lt;INT&gt;</font></code> 记号将值“包”起来；如果是后者，则当作 <code><font face="新宋体">&lt;EOF&gt;</font></code> ；并将记号返回给 <code><font face="新宋体">integerLiteral()</font></code> 做进一步处理。如果记号赋予器未遇到这两个记号，则返回词法错误。 
</li>
				<li>如果记号赋予器返回的记号不是整数记号或 <code><font face="新宋体">&lt;EOF&gt;</font></code> ，那么 <code><font face="新宋体">integerLiteral()</font></code> 抛出 <code><font face="新宋体">ParseException</font></code> ，同时解析完成。 
</li>
				<li>如果它是整数记号，表达式仍然可能是有效的， <code><font face="新宋体">integerLiteral()</font></code> 再次调用记号赋予器以返回下一个记号。如果返回 <code><font face="新宋体">&lt;EOF&gt;</font></code> ，则由单个整数构成的整个表达式都是有效的，解析器将控制返还给调用应用程序。 
</li>
				<li>如果记号赋予器返回加号或减号记号，则表达式仍然是有效的， <code><font face="新宋体">integerLiteral()</font></code> 将最后一次调用记号赋予器，以寻找另一个整数。如果遇到一个整数，则表达式是有效的，解析器将完成工作。如果下一个记号不是整数，则解析器抛出异常。 </li>
		</ol>
		<p>注：如果解析器失败了，则抛出 <code><font face="新宋体">ParseException</font></code> 或 <code><font face="新宋体">TokenMgrError</font></code> 。任何一种异常都表明您的表达式是无效的。 </p>
		<p>这里的 <a name="keypt">要点</a> 是，只有当解析器成功地遍历了嵌入 Java 操作的那部分结果后，才能执行嵌入到这两个结果中的任何 Java 操作。如果将表达式“42 + 1”传递给该解析器，则语句 <code><font face="新宋体">integer = 42</font></code> 将被打印到控制台，后跟 <code><font face="新宋体">integer = 1</font></code> 。如果运行无效的表达式“42 + abc”，则产生消息 <code><font face="新宋体">integer = 42</font></code> ，后跟 catch 块消息 <code><font face="新宋体">a Token Manager error!</font></code> 。在后一种情形中，解析器只成功地遍历了 <code><font face="新宋体">simpleLang</font></code> 结果中的第一个 <code><font face="新宋体">integerLiteral()</font></code> 项，而未成功遍历第二项： </p>
		<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
				<tbody>
						<tr>
								<td>
										<pre>
												<code class="section">
														<font face="Lucida Console">void simpleLang() : {} { integerLiteral() ( ("+" | "-") integerLiteral() )? &lt;EOF&gt; }</font>
												</code>
										</pre>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>换而言之，第二个 <code><font face="新宋体">integerLiteral()</font></code> 方法未被执行，因为未遇到希望的整数标记。 </p>
		<br />
		<table cellspacing="0" cellpadding="0" width="100%" border="0">
				<tbody>
						<tr>
								<td>
										<img style="WIDTH: 500px; CURSOR: pointer" onclick="javascript:window.open(this.src);" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" onload="javascript:if(this.width&gt;500)this.style.width=500;" />
								</td>
						</tr>
				</tbody>
		</table>
		<table class="no-print" cellspacing="0" cellpadding="0" align="right">
				<tbody>
						<tr align="right">
								<td>
										<table cellspacing="0" cellpadding="0" border="0">
												<tbody>
														<tr>
																<td valign="center">
																		<img style="CURSOR: pointer" onclick="javascript:window.open(this.src);" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" onload="javascript:if(this.width&gt;500)this.style.width=500;" border="0" />
																		<br />
																</td>
																<td valign="top" align="right">
																		<a class="fbox" href="http://www-128.ibm.com/developerworks/cn/xml/x-javacc/part1/#main">
																				<b>
																						<font color="#996699">回页首</font>
																				</b>
																		</a>
																</td>
														</tr>
												</tbody>
										</table>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<br />
		<p>
				<a name="5">
						<span class="atitle">
								<font size="4">JavaCC 编译过程</font>
						</span>
				</a>
		</p>
		<p>当您对 .jj 文件运行 JavaCC 时，它会生成许多 Java 源文件。其中一个是主解析代码 Parser_1.java，当您有一个要解析的表达式时，您将从您的应用程序调用该代码。JavaCC 还创建了其它六个由解析器使用的辅助文件。JavaCC 总共生成了以下七个 Java 文件。前三个是特定于这个特殊语法的；后四个是通用的助手类 Java 文件，无论语法是怎么样的，都会生成这几个文件。</p>
		<ul>
				<li>Parser_1.java 
</li>
				<li>Parser_1Constants.java 
</li>
				<li>Parser_1TokenManager.java 
</li>
				<li>ParseException.java 
</li>
				<li>SimpleCharStream.java 
</li>
				<li>Token.java 
</li>
				<li>TokenMgrError.java </li>
		</ul>
		<p>一旦 JavaCC 生成了这七个 Java 源文件，则可以编译它们并将它们链接到您的 Java 应用程序中。然后可以从您的应用程序代码调用新的解析器，从而将表达式传递给它进行求值。下面是一个样本应用程序，它实例化您的解析器，并且为它提供了一个硬连接在应用程序顶部的表达式。</p>
		<br />
		<a name="code3">
				<b>清单 3：调用第一个解析器</b>
		</a>
		<br />
		<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
				<tbody>
						<tr>
								<td>
										<pre>
												<code class="section">
														<font face="Lucida Console">package examples.example_1;

import examples.example_1.Parser_1;
import examples.example_1.ParseException;
import java.io.StringReader;

public class Example_1
{
    static String expression = "1 + 42";
    
    public static void main( String[] args )
    //--------------------------------------
    {
        new Example_1().parse( expression );
    }

    void parse( String expression )
    //-----------------------------
    {
        Parser_1 parser = new Parser_1( new StringReader( expression ));
        try 
        {
            parser.simpleLang();
        }
        catch( ParseException pe )  {
            System.out.println( "not a valid expression" ); 
        }
        catch( TokenMgrError e ) {
            System.out.println( "a Token Manager error!" );
        }
    }
}
</font>
												</code>
										</pre>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>这里有什么是值得注意的呢？</p>
		<ol>
				<li>要调用 <code><font face="新宋体">Parser_1</font></code> 中的解析代码，需要调用该类中的方法 <code><font face="新宋体">simpleLang()</font></code> 。.jj 文件中的结果顺序通常是无关的，而本案例除外，在本案例中，语法中最顶部的结果名称用于调用解析器。 
</li>
				<li>如果正在传递给解析器代码的表达式不能根据语法合法地构造，则将抛出 <code><font face="新宋体">ParseException</font></code> 或 <code><font face="新宋体">LexicalError</font></code> 。 
</li>
				<li>如果表达式是有效的，则执行嵌入语法各部分的任何 Java 操作，这些语法部分都被成功遍历，就象 <a href="http://www-128.ibm.com/developerworks/cn/xml/x-javacc/part1/#keypt"><font color="#996699">遍历解析器代码</font></a>结尾描述的一样。 </li>
		</ol>
		<br />
		<table cellspacing="0" cellpadding="0" width="100%" border="0">
				<tbody>
						<tr>
								<td>
										<img style="WIDTH: 500px; CURSOR: pointer" onclick="javascript:window.open(this.src);" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" onload="javascript:if(this.width&gt;500)this.style.width=500;" />
								</td>
						</tr>
				</tbody>
		</table>
		<table class="no-print" cellspacing="0" cellpadding="0" align="right">
				<tbody>
						<tr align="right">
								<td>
										<table cellspacing="0" cellpadding="0" border="0">
												<tbody>
														<tr>
																<td valign="center">
																		<img style="CURSOR: pointer" onclick="javascript:window.open(this.src);" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" onload="javascript:if(this.width&gt;500)this.style.width=500;" border="0" />
																		<br />
																</td>
																<td valign="top" align="right">
																		<a class="fbox" href="http://www-128.ibm.com/developerworks/cn/xml/x-javacc/part1/#main">
																				<b>
																						<font color="#996699">回页首</font>
																				</b>
																		</a>
																</td>
														</tr>
												</tbody>
										</table>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<br />
		<p>
				<a name="6">
						<span class="atitle">
								<font size="4">结束语</font>
						</span>
				</a>
		</p>
		<p>这篇文章结束后还有第 2 部分。您将从类似的样本代码开始着手，学习如何使用 JavaCC 的“伙伴”工具 JJTree 来创建在运行时构建解析的解析树表示的解析器，而不是执行嵌入 .jj 脚本的操作。正如您将看到的，这有很多优点。</p>
		<br />
		<table cellspacing="0" cellpadding="0" width="100%" border="0">
				<tbody>
						<tr>
								<td>
										<img style="WIDTH: 500px; CURSOR: pointer" onclick="javascript:window.open(this.src);" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" onload="javascript:if(this.width&gt;500)this.style.width=500;" />
								</td>
						</tr>
				</tbody>
		</table>
		<table class="no-print" cellspacing="0" cellpadding="0" align="right">
				<tbody>
						<tr align="right">
								<td>
										<table cellspacing="0" cellpadding="0" border="0">
												<tbody>
														<tr>
																<td valign="center">
																		<img style="CURSOR: pointer" onclick="javascript:window.open(this.src);" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" onload="javascript:if(this.width&gt;500)this.style.width=500;" border="0" />
																		<br />
																</td>
																<td valign="top" align="right">
																		<a class="fbox" href="http://www-128.ibm.com/developerworks/cn/xml/x-javacc/part1/#main">
																				<b>
																						<font color="#996699">回页首</font>
																				</b>
																		</a>
																</td>
														</tr>
												</tbody>
										</table>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<br />
		<p>
				<a name="resources">
						<span class="atitle">
								<font size="4">参考资料 </font>
						</span>
				</a>
		</p>
		<ul>
				<li>您可以参阅本文在 developerWorks 全球站点上的 <a href="http://www.ibm.com/developerworks/library/x-javacc1.html"><font color="#5c81a7">英文原文</font></a>. <br /><br /></li>
				<li>参与有关本文的 <a href="javascript:void forumWindow()"><font color="#5c81a7">论坛</font></a>。（您也可以单击文章顶部或底部的 <b>讨论</b>以访问论坛。） <br /><br /></li>
				<li>希望了解更多有关 BNF 的知识吗？请查阅 <a href="http://www.wikipedia.org/wiki/Backus-Naur_form"><font color="#5c81a7">Wikipedia.org</font></a>。 <br /><br /></li>
				<li>请查阅免费的（但非开放源码） <a href="http://www.webgain.com/products/java_cc/"><font color="#5c81a7">JavaCC 和 JJTree 分发版</font></a>。 <br /><br /></li>
				<li>在 <a href="http://www.w3.org/XML/Query"><font color="#5c81a7">XML Query 主页</font></a>可找到更多有关 W3C 的 XQuery 和 XPath 规范的信息。 <br /><br /></li>
				<li>
						<a href="http://www.fatdog.com/">
								<font color="#5c81a7">XQEngine</font>
						</a>是作者开发的基于 Java 的 XQuery 引擎的开放源码实现。 <br /><br /></li>
				<li>在 developerWorks <a href="http://www.ibm.com/developerworks/xml/"><font color="#5c81a7">XML</font></a>和 <a href="http://www.ibm.com/developerworks/java/"><font color="#5c81a7">Java 技术</font></a>专区可找到本文所涵盖的技术的更多信息。 <br /><br /></li>
				<li>
						<a href="http://www-3.ibm.com/software/info1/websphere/index.jsp?tab=landings/studiosplashv5&amp;origin=x">
								<font color="#5c81a7">IBM WebSphere Studio</font>
						</a>提供了一组使 XML 开发自动化的工具，可使用 Java 也可使用其它语言。它与 <a href="http://www-3.ibm.com/software/webservers/appserv/&amp;origin=x"><font color="#5c81a7">WebSphere Application Server</font></a>进行了紧密的集成，但是也可以与其它 J2EE 服务器一起使用。 <br /><br /></li>
				<li>了解如何才能成为 <a href="http://www-1.ibm.com/certify/certs/adcdxmlrt.shtml&amp;origin=x"><font color="#5c81a7">XML 和相关技术的 IBM 认证开发人员</font></a>。 <br /></li>
		</ul>
		<br />
		<table cellspacing="0" cellpadding="0" width="100%" border="0">
				<tbody>
						<tr>
								<td>
										<img style="WIDTH: 500px; CURSOR: pointer" onclick="javascript:window.open(this.src);" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" onload="javascript:if(this.width&gt;500)this.style.width=500;" />
								</td>
						</tr>
				</tbody>
		</table>
		<table class="no-print" cellspacing="0" cellpadding="0" align="right">
				<tbody>
						<tr align="right">
								<td>
										<table cellspacing="0" cellpadding="0" border="0">
												<tbody>
														<tr>
																<td valign="center">
																		<img style="CURSOR: pointer" onclick="javascript:window.open(this.src);" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" onload="javascript:if(this.width&gt;500)this.style.width=500;" border="0" />
																		<br />
																</td>
																<td valign="top" align="right">
																		<a class="fbox" href="http://www-128.ibm.com/developerworks/cn/xml/x-javacc/part1/#main">
																				<b>
																						<font color="#996699">回页首</font>
																				</b>
																		</a>
																</td>
														</tr>
												</tbody>
										</table>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<br />
		<p>
				<a name="author">
						<span class="atitle">
								<font size="4">关于作者</font>
						</span>
				</a>
		</p>
		<table cellspacing="0" cellpadding="0" width="100%" border="0">
				<tbody>
						<tr>
								<td colspan="2">
										<font size="4">
												<img style="WIDTH: 500px; CURSOR: pointer" onclick="javascript:window.open(this.src);" src="http://www.ibm.com/i/c.gif" width="100%" onload="javascript:if(this.width&gt;500)this.style.width=500;" />
										</font>
								</td>
						</tr>
						<tr valign="top" align="left">
								<td>
										<p>
												<font size="4">
												</font>
										</p>
								</td>
								<td>
										<p>Howard Katz 居住在加拿大温哥华，他是 Fatdog Software 的唯一业主，该公司专门致力于开发搜索 XML 文档的软件。在过去的大约 35 年里，他一直是活跃的程序员（一直业绩良好），并且长期为计算机贸易出版机构撰写技术文章。Howard 是 Vancouver XML Developer's Association 的共同主持人，还是 Addison Wesley 即将出版的书籍 <i>The Experts on XQuery</i>的编辑，该书由 W3C 的 Query 工作组成员合著，概述了有关 XQuery 的技术前景。他和他的妻子夏天去划船，冬天去边远地区滑雪。可以通过 <a href="mailto:howardk@fatdog.com"><font color="#5c81a7">howardk@fatdog.com</font></a>与 Howard 联系。 </p>
								</td>
						</tr>
				</tbody>
		</table>
<img src ="http://www.blogjava.net/landy/aggbug/44766.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/landy/" target="_blank">独孤过客</a> 2006-05-06 16:22 <a href="http://www.blogjava.net/landy/archive/2006/05/06/44766.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>JavaCC 用于支持终端用户对 DB2 UDB 数据库编制简单</title><link>http://www.blogjava.net/landy/archive/2006/05/06/44765.html</link><dc:creator>独孤过客</dc:creator><author>独孤过客</author><pubDate>Sat, 06 May 2006 08:21:00 GMT</pubDate><guid>http://www.blogjava.net/landy/archive/2006/05/06/44765.html</guid><wfw:comment>http://www.blogjava.net/landy/comments/44765.html</wfw:comment><comments>http://www.blogjava.net/landy/archive/2006/05/06/44765.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/landy/comments/commentRss/44765.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/landy/services/trackbacks/44765.html</trackback:ping><description><![CDATA[
		<p>级别: 初级</p>
		<p>
				<a href="http://www-128.ibm.com/developerworks/cn/db2/library/techarticles/dm-0401brereton/index.html#author">
						<font color="#996699">JoAnn P. Brereton</font>
				</a>, 高级软件工程师，IBM<br /></p>
		<p>2004 年 5 月 01 日</p>
		<blockquote>JavaCC 是一个功能极其强大的‘编译器的编译器’工具，可用于编制上下文无关的语法。本文演示了如何将 JavaCC 用于支持终端用户对 DB2 UDB 数据库编制简单的布尔查询。</blockquote>
		<p>
				<a name="0">
						<span class="atitle">
								<strong>
										<font size="4">JavaCC 简介</font>
								</strong>
						</span>
				</a>
		</p>
		<p>许多基于 Web 的项目都包含即席（ad-hoc）查询系统以允许终端用户搜索信息。因此，终端用户会需要某种语言来表达他们所希望搜索的内容。以前，用户查询语言的定义极其简单。如果终端用户满足于使用与最典型的 Google 搜索一般简单的语言，那么 Java 的 StringTokenizer 对于解析任务就绰绰有余了。然而，如果用户希望有一种更健壮的语言，比如要添加括号和“AND”/“OR”逻辑，那么我们很快就会发现我们需要更强大的工具。我们需要一种方法，用以首先定义用户将要使用的语言，然后用该定义解析相应的条目并且对各种后端数据库制定正确的查询。</p>
		<p>这就是工具 JavaCC 出现的原因。JavaCC 代表“Java® Compiler Compiler”，是对 YACC（“Yet Another Compiler Compiler”）的继承（YACC 是 AT&amp;T 为了构建 C 和其他高级语言解析器而开发的一个基于 C 的工具）。YACC 和其伙伴词法记号赋予器（tokenizer）——“Lex”——接收由常用的巴科斯-诺尔范式（Backus-Nauer form，又称 Bacchus Normal Form，BNF）形式的语言定义的输入，并生成一个“C”程序，用以解析该语言的输入以及执行其中的功能。JavaCC 与 YACC 一样，是为加快语言解析器逻辑的开发过程而设计的。但是，YACC 生成 C 代码，而 JavaCC 呢，正如您想像的那样，JavaCC 生成的是 Java 代码。</p>
		<p>JavaCC 的历史极具传奇色彩。它起源于 Sun 公司的“Jack”。Jack 后来辗转了几家拥有者，比如著名的 Metamata 和 WebGain，最后变成了 JavaCC，然后又回到了 Sun。Sun 公司最后在 BSD 的许可下将它作为开放源代码的代码发布。JavaCC 目前的 Web 主页是 <a href="http://javacc.dev.java.net/"><font color="#5c81a7">http://javacc.net.java.net</font></a>。 </p>
		<p>JavaCC 的长处在于它的简单性和可扩展性。要编译由 JavaCC 生成的 Java 代码，无需任何外部 JAR 文件或目录。仅仅用基本的 Java 1.2 版编译器就可以进行编译。而该语言的布局也使得它易于添加产生式规则和行为。该 Web 站点甚至描述了如何编制异常以便给出用户合适的语法提示。</p>
		<br />
		<table cellspacing="0" cellpadding="0" width="100%" border="0">
				<tbody>
						<tr>
								<td>
										<img style="WIDTH: 500px; CURSOR: pointer" onclick="javascript:window.open(this.src);" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" onload="javascript:if(this.width&gt;500)this.style.width=500;" />
								</td>
						</tr>
				</tbody>
		</table>
		<table class="no-print" cellspacing="0" cellpadding="0" align="right">
				<tbody>
						<tr align="right">
								<td>
										<table cellspacing="0" cellpadding="0" border="0">
												<tbody>
														<tr>
																<td valign="center">
																		<img style="CURSOR: pointer" onclick="javascript:window.open(this.src);" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" onload="javascript:if(this.width&gt;500)this.style.width=500;" border="0" />
																		<br />
																</td>
																<td valign="top" align="right">
																		<a class="fbox" href="http://www-128.ibm.com/developerworks/cn/db2/library/techarticles/dm-0401brereton/index.html#main">
																				<b>
																						<font face="Verdana" color="#996699">回页首</font>
																				</b>
																		</a>
																</td>
														</tr>
												</tbody>
										</table>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<br />
		<p>
				<a name="1">
						<span class="atitle">
								<strong>
										<font size="4">问题定义</font>
								</strong>
						</span>
				</a>
		</p>
		<p>让我们假设您有一位客户在一个出租视频节目的商店里，该商店拥有一个简单的电影数据库。该数据库包含表 MOVIES、ACTORS 和 KEYWORDS。MOVIES 表列举他商店中每部电影的相关数据，即如每部电影的名称和导演等内容。ACTORS 表列举每部电影中的演员姓名。而 KEYWORDS 表则列举描述电影的词语，例如“action”、“drama”、“adventure”等等。</p>
		<p>客户希望能够对该数据库发出稍微复杂的查询。例如，他想输入以下形式的查询</p>
		<p>
		</p>
		<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
				<tbody>
						<tr>
								<td>
										<pre>
												<code class="section">
														<font face="Lucida Console"> actor = "Christopher Reeve" and keyword=action and keyword=adventure</font>
												</code>
										</pre>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>并且希望返回由 Christopher Reeve 主演的 Superman 系列电影。他还希望像下面这样用括号来说明求值次序以区分查询</p>
		<p>
		</p>
		<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
				<tbody>
						<tr>
								<td>
										<pre>
												<code class="section">
														<font face="Lucida Console"> (actor = "Christopher Reeve" and keyword=action) or keyword=romance</font>
												</code>
										</pre>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>这样可能返回不是由 Christopher Reeve 主演的电影</p>
		<p>
		</p>
		<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
				<tbody>
						<tr>
								<td>
										<pre>
												<code class="section">
														<font face="Lucida Console">actor = "Christopher Reeve" and (keyword=action or keyword=romance)</font>
												</code>
										</pre>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>这样则总会返回 Christopher Reeve 主演的电影。</p>
		<br />
		<table cellspacing="0" cellpadding="0" width="100%" border="0">
				<tbody>
						<tr>
								<td>
										<img style="WIDTH: 500px; CURSOR: pointer" onclick="javascript:window.open(this.src);" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" onload="javascript:if(this.width&gt;500)this.style.width=500;" />
								</td>
						</tr>
				</tbody>
		</table>
		<table class="no-print" cellspacing="0" cellpadding="0" align="right">
				<tbody>
						<tr align="right">
								<td>
										<table cellspacing="0" cellpadding="0" border="0">
												<tbody>
														<tr>
																<td valign="center">
																		<img style="CURSOR: pointer" onclick="javascript:window.open(this.src);" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" onload="javascript:if(this.width&gt;500)this.style.width=500;" border="0" />
																		<br />
																</td>
																<td valign="top" align="right">
																		<a class="fbox" href="http://www-128.ibm.com/developerworks/cn/db2/library/techarticles/dm-0401brereton/index.html#main">
																				<b>
																						<font face="Verdana" color="#996699">回页首</font>
																				</b>
																		</a>
																</td>
														</tr>
												</tbody>
										</table>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<br />
		<p>
				<a name="2">
						<span class="atitle">
								<strong>
										<font size="4">解决方案</font>
								</strong>
						</span>
				</a>
		</p>
		<p>对于该任务，您将分两个阶段来定义解决方案。在第 1 阶段中，您将用 JavaCC 定义语言，要确保能够正确解析终端用户的查询。在第 2 阶段中，您将向 JavaCC 代码添加行为以产生 DB2® SQL 代码，从而确保返回正确的电影来响应终端用户的查询。</p>
		<p>
				<a name="IDA1LUNB">
						<span class="smalltitle">
								<strong>阶段 1 - 定义用户的查询语言</strong>
						</span>
				</a>
		</p>
		<p>将在名为 UQLParser.jj 的文件里定义该语言。该文件将被 JavaCC 工具编译成为一组 .java 类型的 Java 类文件。要在 JJ 文件中定义语言，您需要做以下 5 件事：</p>
		<ol>
				<li>定义解析环境 
</li>
				<li>定义“空白” 
</li>
				<li>定义“标记（token）” 
</li>
				<li>按照标记定义语言本身的语法 
</li>
				<li>定义每个解析阶段中将发生的行为 </li>
		</ol>
		<p>您可以通过所展示的代码段定义自己的 UQLParser.jj 文件，也可以通过本文的相关代码进行效仿。对于步骤 1 到 4，在 JavaCCPaper/stage1/src 中使用 UQLParser.jj 的副本。而步骤 5 则在 JavaCCPaper/stage2/src 中进行。样本数据库的 DDL 可以在 JavaCCPaper/moviedb.sql 中找到。如果使用相同的用户标识创建数据库和运行解析器，该实例将运行得最好。Ant 文件（build.xml）可用于加快编译过程。</p>
		<p>
				<b>步骤 1. 定义解析环境</b>
		</p>
		<p>JavaCC .jj 文件通过执行 JavaCC 将被转换为 .java 文件。JavaCC 将获取 .jj 文件里 PARSER_BEGIN 与 PARSER_END 的中间部分并将之复制到 Java 结果文件中。作为解析器设计者，您可以将解析前后所有与解析器有关的动作置于该文件中。您还可以在其中将 Java 代码链接到步骤 4 和 5 中将会定义的解析器动作上。</p>
		<p>在以下所示的实例中，解析器相对比较简单。构造函数 UQLParser 接收一个字符串输入，通过 Java 的 java.io.StringReader 类将其读入，然后调用另一个不可见的构造函数将 StringReader 强制转换为 Reader。这里定义的惟一其他方法就是 static main 方法，该方法将在调用构造函数之后再调用迄今还未定义的名为 parse() 的方法。</p>
		<p>正如您可能已猜到的，JavaCC 已经提供了一个 Java Reader 类的构造函数。而我们添加了基于字符串的构造函数，以便易于使用和测试。</p>
		<a name="IDAQMUNB">
				<b>清单 1. 解析器的 Java 环境</b>
		</a>
		<br />
		<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
				<tbody>
						<tr>
								<td>
										<pre>
												<code class="section">
														<font face="Lucida Console">PARSER_BEGIN(UQLParser)
package com.demo.stage1;
import java.io.StringReader;
import java.io.Reader;
public class UQLParser {
/**  
  A String based constructor for ease of use.
  **/
    public UQLParser(String s) 
    {
        this((Reader)(new StringReader(s))); 
        
    }
       
    public static void main(String args[])
    {
        try
        {
              String query = args[0];
              UQLParser parser = new UQLParser(query);
              parser.parse();
        }
        catch(Exception e)
        {
              e.printStackTrace();
        }
    }
}
PARSER_END(UQLParser)
</font>
												</code>
										</pre>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>
				<b>步骤 2. 定义空白</b>
		</p>
		<p>在该语言中，您希望将空格、跳格、回车和换行作为分隔符处理，而不是将其忽略。这些字符都被称为 <b><i>空白</i></b>。在 JavaCC 中，我们在 SKIP 区域中定义这些字符，如清单 2 中所示。 </p>
		<a name="IDACNUNB">
				<b>清单 2. 在 SKIP 区域中定义空白</b>
		</a>
		<br />
		<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
				<tbody>
						<tr>
								<td>
										<pre>
												<code class="section">
														<font face="Lucida Console">/** Skip these characters, they are considered "white space" **/
SKIP :                              
{
    " "
    | "\\t"
    | "\\r"
    | "\\n"
}
   </font>
												</code>
										</pre>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>
				<b>步骤 3. 定义标记</b>
		</p>
		<p>接下来，您将定义该语言所识别的标记。 <b><i>标记</i></b>是将对解析程序有意义的解析字符串的最小单位。扫描输入字符串以及判断是何标记的过程称作 <b><i>记号赋予器（tokenizer）</i></b>。在以下查询中， </p>
		<code>
				<font face="Courier" size="1">actor = "Christopher Reeve"</font>
		</code>
		<p>其标记为 
</p>
		<ul>
				<li>actor 
</li>
				<li>= 
</li>
				<li>"Christopher Reeve" </li>
		</ul>
		<p>在您的语言中，您要将 actor 和等号（=）作为该语言中的保留标记，尽管字 <b><i>if</i></b>和 <b><i>instanceof</i></b>在 Java 语言中都是带有特殊意义的保留标记。通过保留字和其他特殊标记，程序员希望解析器会逐字地识别这些字并为其指派特定的意义。如果您正在保留这些字，请继续进行下去并且保留不等号（&lt;&gt;）和左右括号。还要保留名称、导演和关键字以表示用于用户搜索的特定字段。 </p>
		<p>要定义所有这些内容，请使用 JavaCC 的 TOKEN 指令。每个标记的定义都用尖括号（&lt; 和 &gt;）括起来。在冒号（:）的左边给出标记的名称，并在右边给出正则表达式。正则表达式是定义将要匹配的文本部分的方式。在其最简单的形式中，正则表达式可以匹配精确的字符序列。使用下列代码来定义六个匹配精确字的标记和四个匹配符号的标记。当分析器看到任何一个字时，将会用符号 AND、OR、TITLE、ACTOR、DIRECTOR 或 KEYWORD 来加以匹配。在匹配符号之后，解析器将相应地返回 LPAREN、RPAREN、EQUALS 或 NOTEQUAL。清单 3 展示了 JavaCC 保留标记的定义。</p>
		<a name="IDAIOUNB">
				<b>清单 3. 定义保留标记</b>
		</a>
		<br />
		<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
				<tbody>
						<tr>
								<td>
										<pre>
												<code class="section">
														<font face="Lucida Console">TOKEN: /*RESERVED TOKENS FOR UQL */
{
      &lt;AND: "and"&gt;
   |  &lt;OR:  "or"&gt;
   |  &lt;TITLE: "title"&gt;
   |  &lt;ACTOR: "actor"&gt;
   |  &lt;DIRECTOR: "director"&gt;
   |  &lt;KEYWORD: "keyword"&gt;
   |  &lt;LPAREN: "("&gt;
   |  &lt;RPAREN: ")"&gt;
   |  &lt;EQUALS: "="&gt;
   |  &lt;NOTEQUAL: "&lt;&gt;"&gt;
}
</font>
												</code>
										</pre>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>对于像“Christopher Reeve”一样的字符串，您或许无法在我们的语言中将所有的演员姓名存储为保留字。但是，您可以通过使用正则表达式定义的字符模式识别 STRING 或 QUOTED_STRING 类型的标记。 <b><i>正则表达式</i></b>是定义匹配模式的字符串。定义匹配所有字符串或引用字符串的正则表达式要比定义精确的字匹配更具技巧性。 </p>
		<p>您将定义一个由一个或更多字符系列构成的字符串（STRING），其中的有效字符为大小写的 A 到 Z 以及数字 0 到 9。为了简单起见，不考虑定影明星或电影名称的重音字符或其他不规则体。您可以按下列方式将该模式写为一个正则表达式。</p>
		<p>
		</p>
		<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
				<tbody>
						<tr>
								<td>
										<pre>
												<code class="section">
														<font face="Lucida Console"> &lt;STRING : (["A"-"Z", "a"-"z", "0"-"9"])+ &gt;</font>
												</code>
										</pre>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>加号表示围在括号中的模式（从 A 到 Z、a 到 z 或 0 到 9 中的任何字符）可依次出现一次或多次。在 JavaCC 中，您还可以用星号（*）来表示模式的零次或多次出现以及用问号（？）来表示 0 或 1 此重复。</p>
		<p>QUOTED_STRING 就更具技巧性了。如果您定义一个以引号开头，以引号结尾并在其中包含任何其他字符的字符串，那么该字符串就是一个 QUOTED_STRING。其正则表达式为 <code><font face="Courier" size="1">"\\"" (~["\\""])+ "\\""</font></code> ，这肯定有些眼花缭乱的。简单一点理解就是，由于引用字符本身对于 JavaCC 是有意义的，因此我们需要将对它的引用转换为对我们的语言而非 JavaCC 是有意义的。为了转换该引用，我们在它之前使用了一个反斜杠。字符颚化符号（~）意味着并非是针对 JavaCC 记号赋予器的。 <code><font face="Courier" size="1">(~["\\""])+</font></code> 是对于一个或更多非引用字符的速写。合在一起， <code><font face="Courier" size="1">"\\"" (~["\\""])+ "\\""</font></code> 就意味着“一个引用加上一个或更多非引用再加上一个引用”。 </p>
		<p>您必须在保留字规则之后添加 STRING 和 QUOTED_STRING 的记号赋予器规则。保持该次序是极其重要的，因为记号赋予器规则在文件中出现的次序就是应用标记规则的次序。您需要确定“title”是被视作保留字而非字符串的。清单 4 中显示了 STRING 和 QUOTED_STRING 标记的完整定义。</p>
		<a name="IDAMPUNB">
				<b>清单 4. 定义 STRING 和 QUOTED_STRING</b>
		</a>
		<br />
		<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
				<tbody>
						<tr>
								<td>
										<pre>
												<code class="section">
														<font face="Lucida Console">TOKEN : 
{
&lt;STRING : (["A"-"Z", "0"-"9"])+ &gt;
&lt;QUOTED_STRING: "\\"" (~["\\""])+ "\\"" &gt;
}
</font>
												</code>
										</pre>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>
				<b>步骤 4. 按照标记定义语言</b>
		</p>
		<p>既然已经定义了标记，那么现在是时候按照标记来定义解析规则了。用户输入表达式形式的查询。一个 <b><i>表达式</i></b>就是一系列由 <b><i>布尔运算符</i></b><b><i>and</i></b>或 <b><i>or</i></b>连接的一个或更多查询项。 </p>
		<p>为了表达这一点，我们需要编写一个解析规则，也称作 <b><i>产生式</i></b>。将清单 5 中的产生式写入 JavaCC UQLParser.JJ 文件。 </p>
		<a name="IDANQUNB">
				<b>清单 5. expression() 产生式</b>
		</a>
		<br />
		<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
				<tbody>
						<tr>
								<td>
										<pre>
												<code class="section">
														<font face="Lucida Console">void expression() :
{
}
{            queryTerm()
            (
              ( &lt;AND&gt; | &lt;OR&gt; )
              queryTerm() )*
}
</font>
												</code>
										</pre>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>当对 .jj 文件运行 Javacc 时，产生式将被转换为方法。所有的 JavaCC 产生式方法的返回都必须为空。第一组花括号包含产生式方法所需的所有声明。这里暂时为空。第二组花括号包含以 JavaCC 理解的方式所写的产生式规则。请注意先前所定义的 AND 和 OR 标记的用法。还请注意，queryTerm() 是作为方法调用而写的。实际上，queryTerm() 是另一个产生式方法。</p>
		<p>现在，就让我们定义 queryTerm() 产生式。queryTerm() 要么是一个单独的判别式（例如 title="The Matrix"），要么是一个由括号括起来的表达式。JavaCC 中通过 expression() 递归地定义了 queryTerm()，这使得您可以通过清单 6 中所示的代码简明地总结该语言。</p>
		<a name="IDAZQUNB">
				<b>清单 6. JavaCC 中的 queryTerm() 产生式方法（UQLParser.jj）</b>
		</a>
		<br />
		<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
				<tbody>
						<tr>
								<td>
										<pre>
												<code class="section">
														<font face="Lucida Console">void queryTerm() :
{
}
{
        (&lt;TITLE&gt; | &lt;ACTOR&gt; |
         &lt;DIRECTOR&gt; | &lt;KEYWORD&gt;)
        ( &lt;EQUALS&gt; | &lt;NOTEQUAL&gt;)
        ( &lt;STRING&gt; | &lt;QUOTED_STRING&gt; )
        |
       &lt;LPAREN&gt; expression() &lt;RPAREN&gt;
}
</font>
												</code>
										</pre>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>这就是我们所需的所有规则。两个产生式中总结了全部的语言解析器。</p>
		<p>
				<a name="IDAERUNB">
						<span class="smalltitle">
								<strong>将 JavaCC 当作测试驱动器</strong>
						</span>
				</a>
		</p>
		<p>在这个时候，您应该已经有了一个有效的 JavaCC 文件。在进行到步骤 5 之前，您可以编译并“运行”该程序以查看您的解析器运作是否正确。</p>
		<p>随本文一起提供的 ZIP 文件应包含了阶段 1 的 JavaCC 示例文件 UQLParser.jj。将整个 ZIP 文件解压到一个空目录下。要编译 stage1/UQLParser.jj，您首先需要下载 JavaCC 并根据 <a href="http://javacc.dev.java.net/"><font color="#5c81a7">JavaCC Web 页</font></a> 上的指导进行安装。为了简单起见，请务必将 Javacc.bat 的执行路径填入 PATH 环境变量中。编译十分容易，将目录更改为卸载 UQLParser.jj 的位置并输入下列命令。 </p>
		<code>
				<font face="Courier" size="1">javacc "debug_parser " output_directory=.\\com\\demo\\stage1 UQLParser.jj </font>
		</code>
		<p>如果您愿意，也可以使用附带的 <a href="http://ant.apache.org/"><font color="#5c81a7">Ant</font></a> 文件 build.xml。您必须将上方的属性文件调整为指向 JavaCC 安装。在您第一次运行它时，JavaCC 将生成如清单 7 中所示的消息。 </p>
		<a name="IDAZRUNB">
				<b>清单 7. UQLParser.jj 的编译输出</b>
		</a>
		<br />
		<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
				<tbody>
						<tr>
								<td>
										<pre>
												<code class="section">
														<font face="Lucida Console">Java Compiler Compiler Version 3.2 (Parser Generator)
(type "javacc" with no arguments for help)
Reading from file UQLParser.jj . . .
File "TokenMgrError.java" does not exist.  Will create one.
File "ParseException.java" does not exist.  Will create one.
File "Token.java" does not exist.  Will create one.
File "SimpleCharStream.java" does not exist.  Will create one.
Parser generated successfully.
</font>
												</code>
										</pre>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>除了已提到的四个文件，JavaCC 还将产生 UQLParser.java、UQLParserConstants.java 和 UQLParserTokenManager.java。所有这些文件都被写入了 com\\demo\\stage1 目录。此时起，您就能够编译这些文件且无需向默认的运行时类路径做任何添加了。如果 JavaCC 步骤运行成功，Ant 文件的默认目标将自动执行 Java 编译。如果没有成功，您可以用以下命令编译顶层目录（JavaCCPaper/stage1）的文件：</p>
		<code>
				<font face="Courier" size="1">javac "d bin src\\com\\demo\\stage1\\*.java </font>
		</code>
		<p>Java 类文件一旦就位，您就可以通过向您所定义的 "main" java 方法输入下列用户示例查询来测试新的解析器了。如果您正使用同一代码，请从 JavaCCPaper/stage1 目录开始并在命令行中输入下列命令。</p>
		<code>
				<font face="Courier" size="1">java "cp bin com.demo.stage1.UQLParser "actor = \\"Tom Cruise\\"" </font>
		</code>
		<p>我们在 JavaCC 步骤中所使用的 <code><font face="Courier" size="1">-debug_parser</font></code> 选项确保将输出下列有用的跟踪消息，以显示用户查询是如何被解析的。其输出应该如清单 8 中所示。 </p>
		<a name="IDARSUNB">
				<b>清单 8. 查询 actor="Tom Cruise" 的 UQLParser 输出</b>
		</a>
		<br />
		<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
				<tbody>
						<tr>
								<td>
										<pre>
												<code class="section">
														<font face="Lucida Console">Call:   parse
  Call:   expression
    Call:   queryTerm
      Consumed token: &lt;"actor"&gt;
      Consumed token: &lt;"="&gt;
      Consumed token: &lt;&lt;QUOTED_STRING&gt;: 
          ""Tom Cruise""&gt;
    Return: queryTerm
  Return: expression
  Consumed token: &lt;&lt;EOF&gt;&gt;
Return: parse
</font>
												</code>
										</pre>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>要测试带括号表达式的递归路径，请尝试以下测试。</p>
		<code>
				<font face="Courier" size="1">java "cp bin com.demo.stage1.UQLParser "(actor=\\"Tom Cruise\\" or actor=\\"Kelly McGillis\\") and keyword=drama" </font>
		</code>
		<p>这将产生清单 9 中的输出。</p>
		<a name="IDAATUNB">
				<b>清单 9. 查询 (actor="Tom Cruise" or actor="Kelly McGillis") and keyword=drama 的 UQL1Parser 输出</b>
		</a>
		<br />
		<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
				<tbody>
						<tr>
								<td>
										<pre>
												<code class="section">
														<font face="Lucida Console">Call:   parse
  Call:   expression
    Call:   queryTerm
      Consumed token: &lt;"("&gt;
      Call:   expression
        Call:   queryTerm
          Consumed token: &lt;"actor"&gt;
          Consumed token: &lt;"="&gt;
          Consumed token: &lt;&lt;QUOTED_STRING&gt;: 
                ""Tom Cruise""&gt;
        Return: queryTerm
        Consumed token: &lt;"OR"&gt;
        Call:   queryTerm
          Consumed token: &lt;"actor"&gt;
          Consumed token: &lt;"="&gt;
          Consumed token: &lt;&lt;QUOTED_STRING&gt;:
                ""Kelly McGillis""&gt;
        Return: queryTerm
      Return: expression
      Consumed token: &lt;")"&gt;
    Return: queryTerm
    Consumed token: &lt;"AND"&gt;
    Call:   queryTerm
      Consumed token: &lt;"keyword"&gt;
      Consumed token: &lt;"="&gt;
      Consumed token: &lt;&lt;STRING&gt;: "drama"&gt;
    Return: queryTerm
  Return: expression
  Consumed token: &lt;&lt;EOF&gt;&gt;
Return: parse
</font>
												</code>
										</pre>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>该输出十分有用，因为它演示了通过 queryTerm 和 expression 的递归。queryTerm 的第一个实例实际上就是一个由两个 queryTerm 组成的表达式。 <a href="http://www-128.ibm.com/developerworks/cn/db2/library/techarticles/dm-0401brereton/index.html#fig1"><font color="#996699">图 1</font></a>展示了该解析路径的图形视图。 </p>
		<br />
		<a name="fig1">
				<b>图 1. 解析用户查询的图形表示</b>
		</a>
		<br />
		<img style="WIDTH: 500px; CURSOR: pointer" onclick="javascript:window.open(this.src);" src="http://www-128.ibm.com/developerworks/cn/db2/library/techarticles/dm-0401brereton/javacc-paper-figure1.gif" width="579" onload="javascript:if(this.width&gt;500)this.style.width=500;" />
		<br />如果您对于 JavaCC 生成怎样的 Java 代码感到好奇，就想尽方法看一看（但不要试图进行任何更改！）。您将找到以下内容。 
<p><b>UQLParser.java</b> —— 在这一文件中，您将找到您在 UQLParser.jj 文件里的 PARSER_BEGIN 和 PARSER_END 之间所放置的代码。您还会发现 JJ 产生式方法已经被改变为 Java 方法了。 </p><p>例如，expression() 规则已将被扩展为清单 10 中的代码了。</p><a name="IDAEUUNB"><b>清单 10. UQLParser.java</b></a><br /><table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1"><tbody><tr><td><pre><code class="section"><font face="Lucida Console">static final public void expression() throws ParseException {
  trace_call("expression");
  try {
    queryTerm();
    label_1:
    while (true) {
      switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
      case AND:
      case OR:
        ;
        break;
      default:
        jj_la1[0] = jj_gen;
        break label_1;
      }
      switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
      case AND:
        jj_consume_token(AND);
        break;
      case OR:
        jj_consume_token(OR);
        break;
      default:
        jj_la1[1] = jj_gen;
        jj_consume_token(-1);
        throw new ParseException();
      }
      queryTerm();
    }
  } finally {
    trace_return("expression");
  }
}  
</font></code></pre></td></tr></tbody></table><br /><p>它与您最初在其中写入 queryTerm()、AND 和 OR 所呈现的样子有些相像，但其余的就是 JavaCC 所添加的解析细节。</p><p><b>UQLParserConstants.java</b> —— 该文件易于得到。您所定义的所有标记都在这里。JavaCC 只不过将它们记录在数组中并提供整型常数来引用该数组。清单 11 展示了 UQLParserConstants.java 的内容。 </p><a name="IDASUUNB"><b>清单 11. UQLParserConstants.java</b></a><br /><table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1"><tbody><tr><td><pre><code class="section"><font face="Lucida Console">/* Generated By:JavaCC: Do not edit this line.
UQLParserConstants.java */
package com.demo.stage1;
public interface UQLParserConstants {
  int EOF = 0;
  int AND = 5;
  int OR = 6;
  int TITLE = 7;
  int ACTOR = 8;
  int DIRECTOR = 9;
  int KEYWORD = 10;
  int LPAREN = 11;
  int RPAREN = 12;
  int EQUALS = 13;
  int NOTEQUAL = 14;
  int STRING = 15;
  int QUOTED_STRING = 16;
  int DEFAULT = 0;
  String[] tokenImage = {
    "&lt;EOF&gt;",
    "\\" \\"",
    "\\"\\\\t\\"",
    "\\"\\\\r\\"",
    "\\"\\\\n\\"",
    "\\"and\\"",
    "\\"or\\"",
    "\\"title\\"",
    "\\"actor\\"",
    "\\"director\\"",
    "\\"keyword\\"",
    "\\"(\\"",
    "\\")\\"",
    "\\"=\\"",
    "\\"&lt;&gt;\\"",
    "&lt;STRING&gt;",
    "&lt;QUOTED_STRING&gt;",
  };
}
</font></code></pre></td></tr></tbody></table><br /><p><b>UQLParserTokenManager.java</b> —— 这是一个嵌接文件。JavaCC 将该类用作记号赋予器。这是一段确定标记为什么的代码。这里让人感兴趣的首要例程是 GetNextToken。解析器产生式方法将用该例程来判断采用哪条路经。 </p><p><b>SimpleCharStream.java</b> —— UQLParserTokenManager 用该文件来表示将被解析的字符的 ASCII 流。 </p><p><b>Token.java</b> —— 其中提供了 Token 类来表示标记本身。本文的下一部分将演示 Token 类的用途。 </p><p><b>TokenMgrError.java and ParseException</b>—— 这些类分别表示记号赋予器和分析器中的异常状况。 </p><p><a name="IDAIVUNB"><span class="smalltitle"><strong>阶段 2 - 给 JavaCC 代码添加行为</strong></span></a></p><p><strong>注意：</strong>关于教程的这一部分，请查阅代码的 stage2 子目录。从这里开始所展示的 JJ 文件就是 JavaCCPaper/stage2/UQLParser.jj。为了运行示例 SQL 查询，您还应该通过附带的 moviedb.sql 文件创建 MOVIEDB 数据库。请通过 <code><font face="Courier" size="1">db2 -tf moviedb.sql</font></code> 执行 DDL。 </p><p>既然已经进行了解析，我们就需要对单个表达式采取行动了。这一阶段的目标是生成可运行的 DB2 SQL 查询并将返回用户期望的结果。</p><p>该过程应该首先从一个包含空白处的模板 SELECT，解析器将填写此空白处。 <a href="http://www-128.ibm.com/developerworks/cn/db2/library/techarticles/dm-0401brereton/index.html#listing12"><font color="#996699">清单 12</font></a>中显示了 SELECT 模板。解析器所生成的查询或许不像人类 DBA 所写的那样为最佳的，但是它将返回终端用户所期望的正确结果。 </p><a name="listing12"><b>清单 12. SELECT 语句</b></a><br /><table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1"><tbody><tr><td><pre><code class="section"><font face="Lucida Console">SELECT TITLE, DIRECTOR 
FROM MOVIE
WHERE MOVIE_ID IN
(
         -- parser will fill in here-- 
);
</font></code></pre></td></tr></tbody></table><br /><p>解析器填入的内容取决于它通过记号赋予器所采用的路径。例如，如果用户从上面输入查询：</p><code><font face="Courier" size="1">(actor="Tom Cruise" or actor="Kelly McGillis") and keyword=drama" </font></code><p>那么解析器将根据 <a href="http://www-128.ibm.com/developerworks/cn/db2/library/techarticles/dm-0401brereton/index.html#fig2"><font color="#996699">图 2</font></a> 在 SQL 查询的遗漏部分中发出文本。它将回送括号，输入终端 queryTerm 的子查询并且用 INTERSECT 代替 AND 以及 UNION 代替 OR。 </p><br /><a name="fig2"><b>图 2. SQL 查询的解析器输出</b></a><br /><img style="WIDTH: 500px; CURSOR: pointer" onclick="javascript:window.open(this.src);" src="http://www-128.ibm.com/developerworks/cn/db2/library/techarticles/dm-0401brereton/javacc-paper-figure2.gif" width="597" onload="javascript:if(this.width&gt;500)this.style.width=500;" /><br /><p>这将确保 SQL 查询发出</p><code><font face="Courier" size="1">(actor = "Tom Cruise" or actor = "Kelly McGillis") and keyword=drama </font></code><p>将如清单 13 中所示。</p><a name="IDADXUNB"><b>清单 13. 完整的 SELECT 语句</b></a><br /><table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1"><tbody><tr><td><pre><code class="section"><font face="Lucida Console">SELECT TITLE, DIRECTOR
FROM MOVIE
WHERE MOVIE_ID IN
(
 (
  SELECT MOVIE_ID
  FROM ACTOR
  WHERE NAME='Tom Cruise'
  UNION
  SELECT
  MOVIE_ID
  FROM ACTOR
  WHERE NAME='kelly McGillis'
 )
      INTERSECT
      SELECT MOVIE_ID
 FROM KEYWORD
 WHERE KEYWORD='drama'
);
</font></code></pre></td></tr></tbody></table><br /><p>正如前面提到的，很可能存在更快、更优的方法来编写这个特定的查询，但是此 SQL 将生成正确的结果。DB2 优化器通常可以解决性能方面的不足。</p><p>因此，需要向 JavaCC 源代码添加什么来生成该查询呢？您必须添加动作和其他支持所定义语法的代码。 <b><i>动作</i></b>是指为响应特定产生式而执行的 Java 代码。在添加动作之前，首先要添加将向调用程序返回完整 SQL 的方法。为此，要在 JavaCC 文件的最上部分添加一个名为 <code><font face="Courier" size="1">getSQL()</font></code> 的方法。您还应该给解析器的内部成员添加 private StringBuffer sqlSB。该变量将表示任何解析阶段的当前 SQL 字符串。 <a href="http://www-128.ibm.com/developerworks/cn/db2/library/techarticles/dm-0401brereton/index.html#listing14"><font color="#996699">清单 14</font></a> 展示了 UQLParser.jj 的 PARSER_BEGIN/PARSER_END 部分。最后，在 main() 测试方法中添加一些代码，用以输出和执行所生成的 SQL 查询。 </p><a name="listing14"><b>清单 14. PARSER_BEGIN/PARSER_END 部分</b></a><br /><table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1"><tbody><tr><td><pre><code class="section"><font face="Lucida Console">PARSER_BEGIN(UQLParser)
package com.demo.stage2;
import java.sql.DriverManager;
import java.sql.Connection;
import java.sql.Statement;
import java.sql.ResultSet;
import java.sql.Statement;
import java.lang.StringBuffer;
import java.io.StringReader;
import java.io.Reader;
public class UQLParser {
  
    private static StringBuffer sqlSB;
                   // internal SQL representation.
    
    public UQLParser(String s)
    {
        this((Reader)(new StringReader(s)));
        sqlSB = new StringBuffer();
    }
    
    public String getSQL()
    {
        return sqlSB.toString();
    }
       
    public static void main(String args[])
    {
        try
        {
                String query = args[0];
                UQLParser parser =
                  new UQLParser(query);
                parser.parse();
                System.out.println("\\nSQL Query: " +
                 parser.getSQL());
                
                // Note: This code assumes a
                //  default connection
                // (current userid and password).
                System.out.println("\\nResults of Query");
                
                Class.forName(
                    "COM.ibm.db2.jdbc.app.DB2Driver"
                    ).newInstance();
                Connection con = 
                 DriverManager.getConnection(
                  "jdbc:db2:moviedb");
                Statement stmt =
                 con.createStatement();
                ResultSet rs =
                 stmt.executeQuery(parser.getSQL());
                while(rs.next())
                {
                        System.out.println("Movie Title = " +
                         rs.getString("title") +
                          " Director = " +
                          rs.getString("director"));
                }
                rs.close();
                stmt.close();
                con.close();
        }
        catch(Exception e)
        {
                e.printStackTrace();
        }
    }
}
PARSER_END(UQLParser)
   </font></code></pre></td></tr></tbody></table><br /><p>现在，填入由解析器来完成的动作。我们将先从一个容易的开始。在解析一个表达式的时候，解析器每当解析“AND”时就发出字“INTERSECT”，而解析“OR”时就发出“UNION”。为此，要在 expression 产生式中的 &lt;AND&gt; 和 &lt;OR&gt; 标记之后插入自包含的 Java 代码块。该代码应向 sqlSB StringBuffer 追加 INTERSECT 或 UNION。清单 15 中显示了这些代码。</p><a name="IDAJYUNB"><b>清单 15. expression 所执行的动作</b></a><br /><table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1"><tbody><tr><td><pre><code class="section"><font face="Lucida Console">void expression() :
{
}
{          queryTerm()
          (
            ( &lt;AND&gt;
              { sqlSB.append("\\nINTERSECT\\n"); }
             | &lt;OR&gt;
              { sqlSB.append("\\nUNION\\n"); }
            ) 
            queryTerm() )*
}
   </font></code></pre></td></tr></tbody></table><br /><p></p><table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1"><tbody><tr><td><pre><code class="section"><font face="Lucida Console">queryTerm()</font></code></pre></td></tr></tbody></table><br />产生式内需要执行多个动作。这些任务如下： <ol><li>将搜索名称映射到它们各自的 DB2 表和列上 
</li><li>保存比较器（comparator）标记 
</li><li>将比较字（comparand）转换为 DB2 可以理解的形式，例如，除去 QUOTED_STRING 标记的双引号 
</li><li>向 sqlSB 发送合适的子查询 
</li><li>对于递归表达式的情况，随之发出括号。 </li></ol><p>对于所有这些任务，您将需要一些局部变量。如清单 16 中所示，这些变量是在产生式中第一对花括号之间定义的。</p><a name="IDAAZUNB"><b>清单 16. queryTerm() 的局部变量</b></a><br /><table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1"><tbody><tr><td><pre><code class="section"><font face="Lucida Console">void queryTerm() :
{
      Token tSearchName, tComparator, tComparand;
      String sComparand, table, columnName;
}
</font></code></pre></td></tr></tbody></table><br /><p>第一个任务可用清单 17 中的代码来完成。设置与所遇标记关联的合适的 DB2 表和列。</p><a name="IDALZUNB"><b>清单 17. 将搜索名称映射到 DB2</b></a><br /><table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1"><tbody><tr><td><pre><code class="section"><font face="Lucida Console">      ( 
        &lt;TITLE&gt;    {table = "movie";
           columnName = "title"; } |
        &lt;DIRECTOR&gt; {table = "movie";
       columnName = "director"; } |
        &lt;KEYWORD&gt;  {table = "keyword";
            columnName = "keyword"; } |
        &lt;ACTOR&gt;    {table = "actor";
            columnName = "name"; }
        )
        </font></code></pre></td></tr></tbody></table><br /><p>第二个任务可用清单 18 中的代码来完成。保存标记以便可用于 SQL 缓冲区。</p><a name="IDAWZUNB"><b>清单 18. 保存比较器</b></a><br /><table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1"><tbody><tr><td><pre><code class="section"><font face="Lucida Console">    ( tComparator=&lt;EQUALS&gt; | 
      tComparator=&lt;NOTEQUAL&gt; )
</font></code></pre></td></tr></tbody></table><br /><p>第三个任务可用清单 19 中的代码来完成。相应地设置比较字的值，如果有必要，就从 QUOTED_STRING 标记中除去双引号。</p><a name="IDAB0UNB"><b>清单 19. 准备比较字</b></a><br /><table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1"><tbody><tr><td><pre><code class="section"><font face="Lucida Console">        tComparand=&lt;STRING&gt;  { 
             sComparand = tComparand.image; } 
            |
        tComparand=&lt;QUOTED_STRING&gt; 
        {     // need to get rid of quotes.
             sComparand =
                tComparand.image.substring(1,
             tComparand.image.length() - 1);
         }
   </font></code></pre></td></tr></tbody></table><br /><p>第四个任务可用清单 20 中的代码来完成。完整的查询项被追加到了 sql 缓冲区。</p><a name="IDAM0UNB"><b>清单 20. 编写 SQL 表达式</b></a><br /><table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1"><tbody><tr><td><pre><code class="section"><font face="Lucida Console">    {
         sqlSB.append("SELECT MOVIE_ID FROM ").append(table);
         sqlSB.append("\\nWHERE ").append(columnName);
         sqlSB.append(" ").append(tComparator.image);
         sqlSB.append(" '").append(sComparand).append("'");
   }                
   </font></code></pre></td></tr></tbody></table><br /><p>最后对于递归表达式的情况，当解析器在表达式递归中看到括号时，就应该简单地进行回送，如清单 21 中所示。</p><a name="IDAX0UNB"><b>清单 21. 回送括号</b></a><br /><table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1"><tbody><tr><td><pre><code class="section"><font face="Lucida Console">    &lt;LPAREN&gt;
    { sqlSB.append("("); }
    expression()
    &lt;RPAREN&gt;
    { sqlSB.append(")"); }
</font></code></pre></td></tr></tbody></table><br /><p>清单 22 展示了完整的 queryTerm() 产生式。</p><a name="IDAC1UNB"><b>清单 22. 完整的 queryTerms() 产生式</b></a><br /><table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1"><tbody><tr><td><pre><code class="section"><font face="Lucida Console">/**
 * Query terms may consist of a parenthetically 
 * separated expression or may be a query criteria
 * of the form queryName = something or
 * queryName &lt;&gt; something.
 *
 */
void queryTerm() :
{
        Token tSearchName, tComparator, tComparand;
        String sComparand, table, columnName;
}
{
      ( 
        &lt;TITLE&gt;    {table = "movie";
         columnName = "title"; } |
        &lt;DIRECTOR&gt; {table = "movie";
         columnName = "director"; } |
        &lt;KEYWORD&gt;  {table = "keyword";
         columnName = "keyword"; } |
        &lt;ACTOR&gt;    {table = "actor"; 
         columnName = "name"; }
      )
      
      ( tComparator=&lt;EQUALS&gt; |
        tComparator=&lt;NOTEQUAL&gt; ) 
      
      ( 
        tComparand=&lt;STRING&gt; 
         { sComparand = tComparand.image; }  |
        tComparand=&lt;QUOTED_STRING&gt;
        {     // need to get rid of quotes.
              sComparand = tComparand.image.substring(1,
               tComparand.image.length() - 1);
        }
      )
        
      { 
          sqlSB.append("SELECT MOVIE_ID FROM ").append(table);
          sqlSB.append("\\nWHERE ").append(columnName);
          sqlSB.append(" ").append(tComparator.image);
          sqlSB.append(" '").append(sComparand).append("'");
      }
      |
      &lt;LPAREN&gt;
      { sqlSB.append("("); }
      expression() 
      &lt;RPAREN&gt;        
      { sqlSB.append(")"); }
}
   </font></code></pre></td></tr></tbody></table><br /><p>像前面一样编译并运行 UQLParser.jj。访问 UQLParser.java 并注意产生式规则是如何被整齐地插入生成代码中的。清单 23 中展示了一个 expression() 方法的扩展实例。请注意 jj_consume_token 调用之后的代码。</p><a name="IDAN1UNB"><b>清单 23. UQLParser.java 中的 expression() 方法</b></a><br /><table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1"><tbody><tr><td><pre><code class="section"><font face="Lucida Console">/**
 * An expression is defined to be a queryTerm followed by zero
 * or more query terms joined by either an AND or an OR.   If two
 * query terms are joined with  * AND then both conditions must
 * be met.  If two query terms are joined with an OR, then
 * one of the two conditions must be met.  
 */
static final public void expression() throws ParseException {
    trace_call("expression");
    try {
      queryTerm();
      label_1:
      while (true) {
        switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
        case AND:
        case OR:
          ;
          break;
        default:
          jj_la1[0] = jj_gen;
          break label_1;
        }
        switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
        case AND:
          jj_consume_token(AND);
                  sqlSB.append("\\nINTERSECT\\n");
          break;
        case OR:
          jj_consume_token(OR);
                  sqlSB.append("\\nUNION\\n");
          break;
        default:
          jj_la1[1] = jj_gen;
          jj_consume_token(-1);
          throw new ParseException();
        }
        queryTerm();
      }
    } finally {
      trace_return("expression");
    }
  }
</font></code></pre></td></tr></tbody></table><br /><p>按前面一样运行该代码。您必须在 CLASSPATH 中包含 db2java.zip。这次，当您运行</p><code><font face="Courier" size="1">java 'cp bin;c:/sqllib/db2java.zip com.demo.stage2.UQLParser "(actor=\\"Tom Cruise\\" or actor=\\"Kelly McGillis\\") and keyword=drama" </font></code><p>时，它将生成清单 24 中的输出。</p><a name="IDA21UNB"><b>清单 24. 查询 (actor="Tom Cruise" or actor="Kelly McGillis") and keyword=drama 的 UQL2Parser 输出</b></a><br /><table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1"><tbody><tr><td><pre><code class="section"><font face="Lucida Console">Call:   parse
  Call:   expression
    Call:   queryTerm
      Consumed token: &lt;"("&gt;
      Call:   expression
        Call:   queryTerm
          Consumed token: &lt;"actor"&gt;
          Consumed token: &lt;"="&gt;
          Consumed token: &lt;&lt;QUOTED_STRING&gt;:
              ""Tom Cruise""&gt;
        Return: queryTerm
        Consumed token: &lt;"or"&gt;
        Call:   queryTerm
          Consumed token: &lt;"actor"&gt;
          Consumed token: &lt;"="&gt;
          Consumed token: &lt;&lt;QUOTED_STRING&gt;:
              ""Kelly McGillis""&gt;
        Return: queryTerm
      Return: expression
      Consumed token: &lt;")"&gt;
    Return: queryTerm
    Consumed token: &lt;"and"&gt;
    Call:   queryTerm
      Consumed token: &lt;"keyword"&gt;
      Consumed token: &lt;"="&gt;
      Consumed token: &lt;&lt;STRING&gt;: "drama"&gt;
    Return: queryTerm
  Return: expression
  Consumed token: &lt;&lt;EOF&gt;&gt;
Return: parse
SQL Query: SELECT TITLE,DIRECTOR 
FROM MOVIE
WHERE MOVIE_ID IN (
(SELECT MOVIE_ID FROM actor
WHERE name = 'Tom Cruise'
UNION
SELECT MOVIE_ID FROM actor
WHERE name = 'Kelly McGillis')
INTERSECT
SELECT MOVIE_ID FROM keyword
WHERE keyword = 'drama')
Results of Query
Movie Title = Top Gun Director = Tony Scott
Movie Title = Witness Director = Peter Weir
   </font></code></pre></td></tr></tbody></table><br /><p>尝试更多使用您的解析器的查询。试一试使用 NOTEQUAL 标记的查询，比如 actor&lt;&gt;"Harrison Ford"。尝试一些像“title=”一样的非法查询，看看将发生什么情况。通过非常少的几行 JavaCC 代码，您就生成了非常有效的终端用户查询语言。</p><br /><table cellspacing="0" cellpadding="0" width="100%" border="0"><tbody><tr><td><img style="WIDTH: 500px; CURSOR: pointer" onclick="javascript:window.open(this.src);" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" onload="javascript:if(this.width&gt;500)this.style.width=500;" /></td></tr></tbody></table><table class="no-print" cellspacing="0" cellpadding="0" align="right"><tbody><tr align="right"><td><table cellspacing="0" cellpadding="0" border="0"><tbody><tr><td valign="center"><img style="CURSOR: pointer" onclick="javascript:window.open(this.src);" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" onload="javascript:if(this.width&gt;500)this.style.width=500;" border="0" /><br /></td><td valign="top" align="right"><a class="fbox" href="http://www-128.ibm.com/developerworks/cn/db2/library/techarticles/dm-0401brereton/index.html#main"><b><font face="Verdana" color="#996699">回页首</font></b></a></td></tr></tbody></table></td></tr></tbody></table><br /><br /><p><a name="3"><span class="atitle"><strong><font size="4">最后要考虑的问题</font></strong></span></a></p><p>JavaCC 除了提供解析器生成器之外，还提供 JJDOC 工具，用以编制巴科斯-诺尔范式（Bacchus-Nauer Form）表示的语法。JJDOC 可以使您易于向终端用户提供他们所使用语言的描述。例如，在附带代码中提供的 ant 文件有一个“bnfdoc”目标。</p><p>JavaCC 还提供名为 JJTree 的工具。该工具提供树和节点类，使您易于将代码分成单离的解析和动作类。继续该实例，您可以考虑为查询编写一个简单优化器，以消除不必要的 INTERSECT 和 UNION。您可以通过访问解析树的节点以及合并相似的相邻节点（例如，actor="Tom Cruise" 和 actor="Kelly McGillis"）来完成该工作。</p><p>JavaCC 拥有一个丰富的语法库。您在自己编写解析器之前，一定要查看 JavaCC 的 examples 目录，以便可能获取已构建好的解决方案。</p><p>请务必阅读 JavaCC Web 页上的 Frequently Asked Questions 并访问 <a href="http://www.chinablog.com/news:%20comp.compilers.tools.javacc"><font color="#5c81a7">comp.compilers.tools.javacc</font></a> 上的 javacc 新闻组以便更好地理解 JavaCC 的所有功能和特性。 </p><br /><table cellspacing="0" cellpadding="0" width="100%" border="0"><tbody><tr><td><img style="WIDTH: 500px; CURSOR: pointer" onclick="javascript:window.open(this.src);" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" onload="javascript:if(this.width&gt;500)this.style.width=500;" /></td></tr></tbody></table><table class="no-print" cellspacing="0" cellpadding="0" align="right"><tbody><tr align="right"><td><table cellspacing="0" cellpadding="0" border="0"><tbody><tr><td valign="center"><img style="CURSOR: pointer" onclick="javascript:window.open(this.src);" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" onload="javascript:if(this.width&gt;500)this.style.width=500;" border="0" /><br /></td><td valign="top" align="right"><a class="fbox" href="http://www-128.ibm.com/developerworks/cn/db2/library/techarticles/dm-0401brereton/index.html#main"><b><font face="Verdana" color="#996699">回页首</font></b></a></td></tr></tbody></table></td></tr></tbody></table><br /><br /><p><a name="4"><span class="atitle"><strong><font size="4">结束语</font></strong></span></a></p><p>JavaCC 是一个健壮的工具，可用于定义语法并且易于在 Java 商业应用程序中包含该语法的解析和异常处理。通过本文，我们说明了 JavaCC 可用于为数据库系统的终端用户提供一种功能强大却很简单的查询语言。</p><br /><table cellspacing="0" cellpadding="0" width="100%" border="0"><tbody><tr><td><img style="WIDTH: 500px; CURSOR: pointer" onclick="javascript:window.open(this.src);" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" onload="javascript:if(this.width&gt;500)this.style.width=500;" /></td></tr></tbody></table><table class="no-print" cellspacing="0" cellpadding="0" align="right"><tbody><tr align="right"><td><table cellspacing="0" cellpadding="0" border="0"><tbody><tr><td valign="center"><img style="CURSOR: pointer" onclick="javascript:window.open(this.src);" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" onload="javascript:if(this.width&gt;500)this.style.width=500;" border="0" /><br /></td><td valign="top" align="right"><a class="fbox" href="http://www-128.ibm.com/developerworks/cn/db2/library/techarticles/dm-0401brereton/index.html#main"><b><font face="Verdana" color="#996699">回页首</font></b></a></td></tr></tbody></table></td></tr></tbody></table><br /><br /><p><a name="resources"><span class="atitle"><strong><font size="4">参考资料 </font></strong></span></a></p><ul><li>您可以参阅本文在 developerWorks 全球站点上的 <a href="http://www.ibm.com/developerworks/db2/library/techarticle/dm-0401brereton/index.html"><font color="#5c81a7">英文原文</font></a>. <br /><br /></li><li>可在 <a href="https://javacc.dev.java.net/"><font color="#5c81a7">JavaCC 网站</font></a>上找到 JavaCC 程序包。 <br /><br /><br /><br /></li><li>在 Johnson，Stephen C（AT&amp;T Bell Laboratories，Murray Hill，New Jersey 07974）所写论文 YACC： <a href="http://dinosaur.compilertools.net/yacc/index.html"><font color="#5c81a7">Yet Another Compiler Compiler</font></a>中第一次描述了这个可广泛获得的“编译器的编译器”。 <br /><br /><br /><br /></li><li>一篇由 Oliver Ensileng 撰写的 JavaWorld 的好文章 <a href="http://www.javaworld.com/javaworld/jw-12-2000/jw-1229-cooltools.html"><font color="#5c81a7">Build your own Languages with JavaCC</font></a>。 <br /><br /><br /><br /></li><li>Jocelyn Paine 的 <a href="http://www.j-paine.org/jjtree.html"><font color="#5c81a7">Introduction to JJTree</font></a>。 <br /><br /><br /><br /></li><li>可在 <a href="http://en2.wikipedia.org/wiki/Context-free_grammar"><font color="#5c81a7">Wikipedia</font></a> 中找到对于上下文无关语法的很好解释。 <br /><br /><br /></li></ul><br /><table cellspacing="0" cellpadding="0" width="100%" border="0"><tbody><tr><td><img style="WIDTH: 500px; CURSOR: pointer" onclick="javascript:window.open(this.src);" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" onload="javascript:if(this.width&gt;500)this.style.width=500;" /></td></tr></tbody></table><table class="no-print" cellspacing="0" cellpadding="0" align="right"><tbody><tr align="right"><td><table cellspacing="0" cellpadding="0" border="0"><tbody><tr><td valign="center"><img style="CURSOR: pointer" onclick="javascript:window.open(this.src);" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" onload="javascript:if(this.width&gt;500)this.style.width=500;" border="0" /><br /></td><td valign="top" align="right"><a class="fbox" href="http://www-128.ibm.com/developerworks/cn/db2/library/techarticles/dm-0401brereton/index.html#main"><b><font face="Verdana" color="#996699">回页首</font></b></a></td></tr></tbody></table></td></tr></tbody></table><br /><br /><p><a name="author"><span class="atitle"><strong><font size="4">关于作者</font></strong></span></a></p><table cellspacing="0" cellpadding="0" width="100%" border="0"><tbody><tr><td colspan="2"><strong><font size="4"><img style="WIDTH: 500px; CURSOR: pointer" onclick="javascript:window.open(this.src);" src="http://www.ibm.com/i/c.gif" width="100%" onload="javascript:if(this.width&gt;500)this.style.width=500;" /></font></strong></td></tr><tr valign="top" align="left"><td><p><strong><font size="4"></font></strong></p></td><td><p>JoAnn Brereton 是 IBM 的 Software Group，Federal Software Services 的一位高级软件工程师。她已为 IBM 编程近 20 年了。她最近的项目包括为 CBS、Warner Brothers 和 CNN 电视网构建视档案搜索引擎。</p></td></tr></tbody></table><img src ="http://www.blogjava.net/landy/aggbug/44765.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/landy/" target="_blank">独孤过客</a> 2006-05-06 16:21 <a href="http://www.blogjava.net/landy/archive/2006/05/06/44765.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>从lex&amp;yacc说到编译器(6.数学表达式)</title><link>http://www.blogjava.net/landy/archive/2006/05/06/44764.html</link><dc:creator>独孤过客</dc:creator><author>独孤过客</author><pubDate>Sat, 06 May 2006 08:20:00 GMT</pubDate><guid>http://www.blogjava.net/landy/archive/2006/05/06/44764.html</guid><wfw:comment>http://www.blogjava.net/landy/comments/44764.html</wfw:comment><comments>http://www.blogjava.net/landy/archive/2006/05/06/44764.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/landy/comments/commentRss/44764.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/landy/services/trackbacks/44764.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 前言																																																																																				文法分析中最重要算法是																		LL自顶向下和LR自底向上算法.前面几篇文章主要讲解的是LL算法的理论和一个LL算法的文法分析器javacc....&nbsp;&nbsp;<a href='http://www.blogjava.net/landy/archive/2006/05/06/44764.html'>阅读全文</a><img src ="http://www.blogjava.net/landy/aggbug/44764.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/landy/" target="_blank">独孤过客</a> 2006-05-06 16:20 <a href="http://www.blogjava.net/landy/archive/2006/05/06/44764.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>从lex&amp;yacc说到编译器(5.实用javacc)</title><link>http://www.blogjava.net/landy/archive/2006/05/06/44763.html</link><dc:creator>独孤过客</dc:creator><author>独孤过客</author><pubDate>Sat, 06 May 2006 08:18:00 GMT</pubDate><guid>http://www.blogjava.net/landy/archive/2006/05/06/44763.html</guid><wfw:comment>http://www.blogjava.net/landy/comments/44763.html</wfw:comment><comments>http://www.blogjava.net/landy/archive/2006/05/06/44763.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/landy/comments/commentRss/44763.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/landy/services/trackbacks/44763.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 前言																																																																												本系列的文章的宗旨是让大家能够写出自己的编译器,解释器或者脚本引擎,所以每到理论介绍到一个程度后,我都会来讨论实践问题.理论方面,编译原理的教材已经是够多了,而实践的问题却很少讨论. 		...&nbsp;&nbsp;<a href='http://www.blogjava.net/landy/archive/2006/05/06/44763.html'>阅读全文</a><img src ="http://www.blogjava.net/landy/aggbug/44763.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/landy/" target="_blank">独孤过客</a> 2006-05-06 16:18 <a href="http://www.blogjava.net/landy/archive/2006/05/06/44763.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>从lex&amp;yacc说到编译器(4.文法识别(一))</title><link>http://www.blogjava.net/landy/archive/2006/05/06/44762.html</link><dc:creator>独孤过客</dc:creator><author>独孤过客</author><pubDate>Sat, 06 May 2006 08:17:00 GMT</pubDate><guid>http://www.blogjava.net/landy/archive/2006/05/06/44762.html</guid><wfw:comment>http://www.blogjava.net/landy/comments/44762.html</wfw:comment><comments>http://www.blogjava.net/landy/archive/2006/05/06/44762.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/landy/comments/commentRss/44762.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/landy/services/trackbacks/44762.html</trackback:ping><description><![CDATA[
		<div>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">没想到这一系列文件能得到<span lang="EN-US">csdn和大家的这么看好,首先要感谢大家的赏识和csdn的推荐.那么我就更没有理由不写好这下面的几篇文章了.本来我的计划是简单把lex和yacc介绍完后就直接进入编译器的构造的技术细节问题讨论,但是最近看了一些国外经典教材后,发现文法的识别问题在编译原理和技术中是个绝不能忽视的问题.即使现在有了yacc工具来帮助我来识别文法,但是有些时候还是需要我们自己来写简单的语法分析器. 
<p></p></span></span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<b>
								<span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">
										<p>
										</p>
								</span>
						</b>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<b>
								<span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">1.什么是文法识别(语法分析) 
<p></p></span>
						</b>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">首先要告诉大家的是<span lang="EN-US">,这里的文法识别是指的上下文无关的文法,也就是上一节我们一直在讨论的那些 BNF式. 
<p></p></span></span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">
								<p>
								</p>
						</span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">比如说<span lang="EN-US">,我写了一句 
<p></p></span></span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">if (a&gt;6+5) printf(“OK!”); else printf(“No!”); 
<p></p></span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">那么它匹配的文法也就是<span lang="EN-US"><p></p></span></span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<i>
								<span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">if-stmt -&gt; <b>if</b> expr stmt 
<p></p></span>
						</i>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<i>
								<span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">
										<span style="mso-spacerun: yes">         </span>| <b>if</b> expr stmt <b>else</b> stmt 
<p></p></span>
						</i>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">我们通常要为一个程序语言写出很多<span lang="EN-US">BNF式的文法,怎么知道这句话是匹配的哪个文法,这就是语法分析器(或者叫文法分析器要做的工作).知道了是那句文法后,我们才能对这句话做出正确的解释,所以文法识别是个不可忽视的工作.下面我来看看我们常使用的文法识别的算法. 
<p></p></span></span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">
								<p>
								</p>
						</span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<b>
								<span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">2.自顶向下的算法(LL算法) 
<p></p></span>
						</b>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">自顶向下的语法分析算法是十分简单的<span lang="EN-US">.自顶向下的算法也叫LL算法.LL(k)就是向前预测k个符号的自顶向下的算法.不过无论是我们国内的编译教程还是国外的经典教程都是只讨论了LL(1)算法.因为一般的程序语言,只使用LL(1)算法就已经足够了.这里我们同样也只是讨论LL(1)算法. 
<p></p></span></span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">
								<p>
								</p>
						</span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">其中有种特殊的算法叫做递归下降的算法<span lang="EN-US">,在C语言中,由于函数本身是可以递归的,所以实现这种算法就只需要写简单的几个函数的递归过程就是了. 
<p></p></span></span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">为什么叫自顶向下呢<span lang="EN-US">?因为在分析过程中,我们是从语法树的树顶逐步向树底分析的,所以叫自顶向下的算法. 
<p></p></span></span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">
								<p>
								</p>
						</span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">为了方便说明自顶向下算法的简单性<span lang="EN-US">,我们来看一下&lt;&lt;Compilers Principles,Techniques,and Tools&gt;&gt;中的一个例子.(本系列文章经常要引用国外经典著作的范例,希望大家不要告我抄袭,我实在找不到比大师的范例更经典的范例了) 
<p></p></span></span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">
								<p>
								</p>
						</span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span style="FONT-SIZE: 10pt; COLOR: blue; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">例<span lang="EN-US">4.1 
<p></p></span></span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">考虑一个<span lang="EN-US">Pascal中定义变量的文法. 
<p></p></span></span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">
								<p>
								</p>
						</span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">特别说明<span lang="EN-US">,这里的<b>dotdot</b>表示”..” 
<p></p></span></span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<i>
								<span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">type</span>
						</i>
						<span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt"> -&gt; <i>simple</i> | <b>id</b> | <b>array</b> [ <i>simple</i> ] <b>of</b><i>type 
<p></p></i></span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<i>
								<span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">simple -&gt; </span>
						</i>
						<b>
								<span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">integer</span>
						</b>
						<i>
								<span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">
								</span>
						</i>
						<span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">|<i></i><b>char</b><i></i>|<b> num dotdot num 
<p></p></b></span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">
								<p>
								</p>
						</span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">在为<b><span lang="EN-US">array[ num dotdot num] of integer</span></b>构造一个分析数的时候<span lang="EN-US">,该算法就是从根结点开始. 
<p></p></span></span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">下面我们通过其中主要的三个步骤来看看算法的实现原理<span lang="EN-US">. 
<p></p></span></span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<b>
								<i>
										<span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">
												<p>
												</p>
										</span>
								</i>
						</b>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<b>
								<i>
										<span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">第一步分析<span lang="EN-US">: 
<p></p></span></span>
								</i>
						</b>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<b>
								<i>
										<span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">__________________________________________________________________________________ 
<p></p></span>
								</i>
						</b>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">
								<shapetype id="_x0000_t75" coordsize="21600,21600" o:spt="75" o:preferrelative="t" path="m@4@5l@4@11@9@11@9@5xe" filled="f" stroked="f">
										<stroke joinstyle="miter">
										</stroke>
										<formulas>
												<f eqn="if lineDrawn pixelLineWidth 0">
												</f>
												<f eqn="sum @0 1 0">
												</f>
												<f eqn="sum 0 0 @1">
												</f>
												<f eqn="prod @2 1 2">
												</f>
												<f eqn="prod @3 21600 pixelWidth">
												</f>
												<f eqn="prod @3 21600 pixelHeight">
												</f>
												<f eqn="sum @0 0 1">
												</f>
												<f eqn="prod @6 1 2">
												</f>
												<f eqn="prod @7 21600 pixelWidth">
												</f>
												<f eqn="sum @8 21600 0">
												</f>
												<f eqn="prod @7 21600 pixelHeight">
												</f>
												<f eqn="sum @10 21600 0">
												</f>
										</formulas>
										<path o:extrusionok="f" gradientshapeok="t" o:connecttype="rect">
										</path>
										<lock v:ext="edit" aspectratio="t">
										</lock>
								</shapetype>
								<shape id="_x0000_i1025" style="WIDTH: 414.75pt; HEIGHT: 112.5pt" type="#_x0000_t75">
										<img style="CURSOR: pointer" onclick="javascript:window.open(this.src);" src="http://blog.csdn.net/Develop/ArticleImages/21/21722/CSDN_Dev_Image_2003-10-231448120.jpg" onload="javascript:if(this.width&gt;500)this.style.width=500;" o:title="1" />
								</shape>
								<p>
								</p>
						</span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">首先分析的是输入的字符串第一个串<span lang="EN-US">”array”,判断出它属于type的<b>First</b>集合.所以在图中的分析树部分,我们的当前分析就是树根结点type.(图中标上箭头,就表示是当前正在分析的部分). 
<p></p></span></span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">这里遇到一个新名词<span lang="EN-US">:<b>First</b>集合.在大学里的编译课程肯定是讲过First集合的吧.不过我还是要在这里重复一次了. 
<p></p></span></span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">
								<p>
								</p>
						</span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span style="FONT-SIZE: 10pt; COLOR: blue; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">名词解释<b><span lang="EN-US">First</span></b>集合<span lang="EN-US">: 
<p></p></span></span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">
								<p>
								</p>
						</span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">在对文法产生式进行判断的时候<span lang="EN-US">,每个产生式都是由好几个终结符和非终结符构成.比如 
<p></p></span></span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">本例中的文法<span lang="EN-US"><p></p></span></span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<i>
								<span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">type</span>
						</i>
						<span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt"> -&gt; <i>simple</i><p></p></span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt; TEXT-INDENT: 40pt; mso-char-indent-count: 4.0; mso-char-indent-size: 10.0pt">
						<span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">| <b>id</b><p></p></span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt; TEXT-INDENT: 40pt; mso-char-indent-count: 4.0; mso-char-indent-size: 10.0pt">
						<span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">| <b>array</b> [ <i>simple</i> ] <b>of</b><i>type 
<p></p></i></span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<i>
								<span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">simple -&gt; </span>
						</i>
						<b>
								<span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">integer</span>
						</b>
						<i>
								<span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">
										<p>
										</p>
								</span>
						</i>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt; TEXT-INDENT: 40pt; mso-char-indent-count: 4.0; mso-char-indent-size: 10.0pt">
						<span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">|<i></i><b>char</b><i><p></p></i></span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt; TEXT-INDENT: 40pt; mso-char-indent-count: 4.0; mso-char-indent-size: 10.0pt">
						<span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">|<b> num dotdot num 
<p></p></b></span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">
								<p>
								</p>
						</span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">判断<span lang="EN-US">type的产生式的时候,如果我们把每个产生式里面的simple,id,array, [ ,simple ,] , of , type这些终结符和非终结符都进行判断的话,那么就会涉及到”试验和错误”的问题.当一个文法产生式分析到最后,发现并不匹配,就必然会产生回溯的问题,就要回到头,从新开始对第二个产生式逐步进行判断分析.我们知道,回溯的算法效率肯定是十分低效率的.但是实际上我们完全可以避免这种回溯算法,而完成同样工作的文法分析.这就产生了计算<b>First</b>集合的理论和以及后面的<b>左提公因式</b>的问题. 
<p></p></span></span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">
								<p>
								</p>
						</span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">First集合简单地说,就是一个非终结符的最开头的字符串(终结符号)的集合.比如说. 
<p></p></span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">First(simple) = { <b>integer, char, num</b> } 
<p></p></span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">First(type) = First(simple) U { <b>id</b>, <b>array</b> } 
<p></p></span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">这里的<span lang="EN-US">type的一个产生式中有个simple非终结符在其开头,那么simple的开头字符串同时也可以是simple,所以First(simple)也是First(type)的一部分. 
<p></p></span></span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">为什么我们只计算每个非终结符的最开头的终结符<span lang="EN-US">? 因为我们这里是考虑的LL(1)算法,LL(1)算法只向前预测一个字符号,所以我们只考虑一个First集合就可以判断出是哪个文法产生式了. 
<p></p></span></span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">
								<p>
								</p>
						</span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">这里听起来似乎有些不太可能<span lang="EN-US">,一个产生式有那么千百万化,如果单单只看第一个非终结符号,如果就能说明一个输入串到底是哪个产生式呢? 如果有两个产生式的最开头一样怎么办,比如像if语句,那怎么办? 但其实我们几乎所有的程序语言的文法都可以通过LL(1)来分析出来.原因是我们可以通过<b>左提公因式</b>来把最开头的相同的产生式的公共终结符号提取出来,留下两个子产生式,而他们的最开头的非终结符号不相同. 
<p></p></span></span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt; tab-stops: 136.55pt">
						<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: blue; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">
								<p>
								</p>
						</span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt; tab-stops: 136.55pt">
						<span style="FONT-SIZE: 10pt; COLOR: blue; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">左提公因式<span lang="EN-US">: 
<p></p></span></span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span style="FONT-SIZE: 10pt; COLOR: blue; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">例<span lang="EN-US">4.2 
<p></p></span></span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">考虑文法<span lang="EN-US"><p></p></span></span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<i>
								<span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">A</span>
						</i>
						<span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt"> -&gt; <b>ab 
<p></p></b></span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt; TEXT-INDENT: 19.9pt; mso-char-indent-count: 1.99; mso-char-indent-size: 10.0pt">
						<span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">|<b>ac</b><p></p></span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">这里<span lang="EN-US">,A的两个产生式中最开头的终结符号都是’<b>a</b>’,那么就无法通过这个最开头的终结符号来判断一个输入串到底该是哪个产生式了.那么我们可以修改文法成 
<p></p></span></span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<i>
								<span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">A </span>
						</i>
						<span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">-&gt; <b>a</b><i>A’</i><p></p></span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<i>
								<span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">A’</span>
						</i>
						<span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">-&gt; <b>b</b> | <b>c 
<p></p></b></span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">这样一来<span lang="EN-US">,一个文法变成两个,但是无论A还是A’,它们的每个产生式的<b>First集合都是不相交的</b>.所以,他们能够只通过最开头的终结符号来判断是哪个产生式. 
<p></p></span></span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">这个变化过程有点想我们的代数里面的<span lang="EN-US"> ab + ac = a(b+c),所以叫它左提公因式. 
<p></p></span></span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">这只是个简单的左提公因式的例子<span lang="EN-US">,实际当中还会遇到一些复杂的问题.但是无论是哪个编译教材,都会给出针对一切文法的左提公因式的算法.同样,计算First集合的算法也在教材中详细讲解了.我就不在这里再描述了. 
<p></p></span></span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">
								<p>
								</p>
						</span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<b>
								<i>
										<span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">第二步分析<span lang="EN-US">: 
<p></p></span></span>
								</i>
						</b>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<b>
								<i>
										<span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">__________________________________________________________________________________ 
<p></p></span>
								</i>
						</b>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">
								<shape id="_x0000_i1026" style="WIDTH: 415.5pt; HEIGHT: 2in" type="#_x0000_t75">
										<img style="CURSOR: pointer" onclick="javascript:window.open(this.src);" src="http://blog.csdn.net/Develop/ArticleImages/21/21722/CSDN_Dev_Image_2003-10-231448122.jpg" onload="javascript:if(this.width&gt;500)this.style.width=500;" o:title="2" />
								</shape>
								<p>
								</p>
						</span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">经过第一步的考察输入串中的第一个串为<span lang="EN-US">”array”属于非终结符号type第三个产生式的First集合,那么就可以确定这个串确实为type文法第三个产生式的串.所以在第二步中,展开出type的第三个产生式出来. <i>type</i> -&gt; <b>array</b><b>[ </b><i>simple</i><b> ]</b><b>of integer</b><p></p></span></span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">那么接下来就是继续分析构造出来的<span lang="EN-US">type -&gt; array[ simple] of integer产生式中的每个结点. 
<p></p></span></span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">所以箭头又放到了分析树中<span lang="EN-US">type的第一个孩结点<b>array</b>上.因为array是终结符号,如果它和输入中的当前箭头所指的终结符号相同,那么箭头都往下移动一结点到’<b>[</b>‘符号.同样地,由于分析树中的’<b>[‘</b>是终结符号,那么只要看输入中的串是否是’<b>[</b>‘就可以了.如果是,那么继续往下分析.分析到分析数中的<i>simple</i>的时候,由于simple是非终结符号,那么就需要考虑simple的产生式了. 
<p></p></span></span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">
								<p>
								</p>
						</span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<b>
								<i>
										<span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">第三步分析<span lang="EN-US">: 
<p></p></span></span>
								</i>
						</b>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<b>
								<i>
										<span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">__________________________________________________________________________________ 
<p></p></span>
								</i>
						</b>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">
								<shape id="_x0000_i1027" style="WIDTH: 414.75pt; HEIGHT: 234.75pt" type="#_x0000_t75">
										<img style="CURSOR: pointer" onclick="javascript:window.open(this.src);" src="http://blog.csdn.net/Develop/ArticleImages/21/21722/CSDN_Dev_Image_2003-10-231448124.jpg" onload="javascript:if(this.width&gt;500)this.style.width=500;" o:title="3" />
								</shape>
								<p>
								</p>
						</span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">在第二步中<span lang="EN-US">,分析到分析数中的simple子结点的时候,由于simple是非终结符号,那么就需要考虑simple的产生式.simple一共有三个产生式.通过输入串当前的串是”<b>num</b>”,是属于simple产生式中第3个产生式的First集合,所以simple在分析数中就按第三个产生式<i>simple</i> -&gt; <b>num dotdot num</b> 来展开.那么分析箭头同样,也自动移动到simple的第一个子结点<b>num</b>上继续分析. 
<p></p></span></span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">
								<p>
								</p>
						</span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">
								<p>
								</p>
						</span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">总体说来<span lang="EN-US">,这中自顶向下的分析原理就基本上是上面的过程.通过计算产生式的First集合,来逐步产生非终结符的产生式.最后的分析树都会划归到终结符来进行判断(非终结符号是无法进行直接判断的,一定要展开过后才行). 
<p></p></span></span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">
								<p>
								</p>
						</span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">看了原理<span lang="EN-US">,我们再看实现的伪代码.代码很简单. 
<p></p></span></span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">________________________________________________________________________ 
<p></p></span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: blue; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">void </span>
						<span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">match(<span style="COLOR: blue">char</span> token) 
<p></p></span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">{ 
<p></p></span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">
								<span style="mso-tab-count: 1">    </span>
								<span style="COLOR: blue">if</span> lookahead == token) 
<p></p></span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt; TEXT-INDENT: 30.75pt">
						<span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">lookahead = token; 
<p></p></span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">
								<span style="mso-tab-count: 1">    </span>
								<span style="COLOR: blue">else 
<p></p></span>
						</span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">
								<span style="mso-tab-count: 1">    </span>
								<span style="mso-spacerun: yes"> </span>error(0); 
<p></p></span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">} 
<p></p></span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">
								<p>
								</p>
						</span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: blue; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">void</span>
						<span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt"> type() 
<p></p></span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">{ 
<p></p></span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">
								<span style="mso-tab-count: 1">    </span>
								<span style="COLOR: blue">if</span>( lookahead == <b>integer</b> || lookeahead == <b>char</b> || lookahead == <b>num</b>) 
<p></p></span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">
								<span style="mso-tab-count: 2">        </span>simple(); 
<p></p></span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">
								<span style="mso-tab-count: 1">    </span>
								<span style="COLOR: blue">else if</span>( lookahead == <b>id</b>) 
<p></p></span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">
								<span style="mso-tab-count: 2">        </span>match(<b>id</b>); 
<p></p></span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">
								<span style="mso-tab-count: 1">    </span>
								<span style="COLOR: blue">else if</span>( lookahead == <b>array</b>) 
<p></p></span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">
								<span style="mso-tab-count: 1">    </span>{ 
<p></p></span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">
								<span style="mso-tab-count: 2">        </span>match(<b>array</b>); match(<b>')'</b>); simple(); match(<b>')'</b>); match(<b>of</b>); type(); 
<p></p></span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">
								<span style="mso-tab-count: 1">    </span>} 
<p></p></span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">
								<span style="mso-tab-count: 1">    </span>
								<span style="COLOR: blue">else 
<p></p></span>
						</span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">
								<span style="mso-tab-count: 2">        </span>error(0); 
<p></p></span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">} 
<p></p></span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">
								<p>
								</p>
						</span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: blue; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">void</span>
						<span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt"> simple() 
<p></p></span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">{ 
<p></p></span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">
								<span style="mso-tab-count: 1">    </span>
								<span style="COLOR: blue">if</span>( lookahead == <b>integar</b>) match(<b>integer</b>); 
<p></p></span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">
								<span style="mso-tab-count: 1">    </span>
								<span style="COLOR: blue">else if</span>( lookahead == <b>char</b>) match(<b>char</b>); 
<p></p></span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">
								<span style="mso-tab-count: 1">    </span>
								<span style="COLOR: blue">else if</span>( lookahead == <b>num</b>) 
<p></p></span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">
								<span style="mso-tab-count: 1">    </span>{ 
<p></p></span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">
								<span style="mso-tab-count: 2">        </span>match(<b>num</b>); match(<b>dotdot</b>); match(<b>num</b>); 
<p></p></span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">
								<span style="mso-tab-count: 1">    </span>} 
<p></p></span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">
								<span style="mso-tab-count: 1">    </span>
								<span style="COLOR: blue">else 
<p></p></span>
						</span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">
								<span style="mso-tab-count: 2">        </span>error(0); 
<p></p></span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">} 
<p></p></span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">_________________________________________________________________________ 
<p></p></span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">注意<span lang="EN-US">:这里的代码都是纯的语法分析代码,实际执行过程中并没有什么用处,但是我们构造语法树parse-tree的代码就是镶嵌在这些纯的语法分析代码中. 
<p></p></span></span>
				</p>
		</div>
<img src ="http://www.blogjava.net/landy/aggbug/44762.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/landy/" target="_blank">独孤过客</a> 2006-05-06 16:17 <a href="http://www.blogjava.net/landy/archive/2006/05/06/44762.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>从lex&amp;yacc说到编译器(3.范式文法)</title><link>http://www.blogjava.net/landy/archive/2006/05/06/44761.html</link><dc:creator>独孤过客</dc:creator><author>独孤过客</author><pubDate>Sat, 06 May 2006 08:16:00 GMT</pubDate><guid>http://www.blogjava.net/landy/archive/2006/05/06/44761.html</guid><wfw:comment>http://www.blogjava.net/landy/comments/44761.html</wfw:comment><comments>http://www.blogjava.net/landy/archive/2006/05/06/44761.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/landy/comments/commentRss/44761.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/landy/services/trackbacks/44761.html</trackback:ping><description><![CDATA[
		<div>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">从这一节开始<span lang="EN-US">,我们就算进入编译器构造的正题了.不得不说,前面的词法扫描器在整个编译器部分只是个很小很小的组成,而这两节讲述的语言构造器才能真正为我们的编译工作起到重要的作用.这些东西相信大家在大学的编译原理的课程已经学了不少,那么本文我也只是大致地带过,让大家回忆起大学的知识,重要的yacc使用技巧等等,我将在后面的内容讲出. 
<p></p></span></span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">
								<p>
								</p>
						</span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span style="FONT-SIZE: 10pt; COLOR: blue; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">例<span lang="EN-US">3.1 
<p></p></span></span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<i>
								<span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">exp -&gt; exp op exp | <b>(</b>exp<b>)</b> |<b> number 
<p></p></b></span>
						</i>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<i>
								<span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">op -&gt;<b> + </b>|<b> -</b> | <b>* 
<p></p></b></span>
						</i>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">
								<p>
								</p>
						</span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">这里就是一个定义的带有加法<span lang="EN-US">,减法,乘法的简单整数算术表达式的文法.其中粗体表示的是终结符号,也就是不能有产生式生成的符号.而exp,op就是非终结符,它们都是由一个”-&gt;”符号来产生的. 
<p></p></span></span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">比如<span lang="EN-US">100 + 222 *123123 –(888+11)就是符合上述文法的具体的表达式. 
<p></p></span></span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">注意<span lang="EN-US">,在文法定义中,是可以递归的.所以exp产生式右边的式子中可以再次出现exp. 
<p></p></span></span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">这里的<span lang="EN-US">|和正则表达式一样,表示的选择的意思,也就是说,exp可以是exp op exp或者(exp)再或者number. 
<p></p></span></span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">
								<p>
								</p>
						</span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">下面让我们看看<span lang="EN-US">&lt;&lt;编译原理及实践&gt;&gt;书中的一个关于BNF文法的介绍. 
<p></p></span></span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">比如说我们有个数学表达式<span lang="EN-US">(34-3)*42,然后我们来看看上面的exp文法怎么来推导识别它. 
<p></p></span></span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">
								<p>
								</p>
						</span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">(1) exp =&gt; exp op exp<span style="mso-spacerun: yes">               </span>[exp -&gt;exp op exp] 
<p></p></span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">(2)<span style="mso-spacerun: yes">     </span>=&gt; exp op number<span style="mso-spacerun: yes">            </span>[exp -&gt;number<span style="mso-spacerun: yes">    </span>] 
<p></p></span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">(3)<span style="mso-spacerun: yes">     </span>=&gt; exp * number<span style="mso-spacerun: yes">             </span>[op -&gt; *<span style="mso-spacerun: yes">         </span>] 
<p></p></span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">(4)<span style="mso-spacerun: yes">     </span>=&gt; (exp) * number<span style="mso-spacerun: yes">           </span>[exp -&gt;(exp)<span style="mso-spacerun: yes">     </span>] 
<p></p></span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">(5)<span style="mso-spacerun: yes">     </span>=&gt; (exp op exp) * number<span style="mso-spacerun: yes">    </span>[exp -&gt;exp op exp] 
<p></p></span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">(6)<span style="mso-spacerun: yes">     </span>=&gt; (exp op number)* number<span style="mso-spacerun: yes">  </span>[exp -&gt; number<span style="mso-spacerun: yes">   </span>] 
<p></p></span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">(7)<span style="mso-spacerun: yes">     </span>=&gt; (exp – number) * number [op -&gt; -<span style="mso-spacerun: yes">         </span>] 
<p></p></span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">(8)<span style="mso-spacerun: yes">     </span>=&gt; (number–number)* number [exp -&gt; number<span style="mso-spacerun: yes">   </span>] 
<p></p></span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">
								<p>
								</p>
						</span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">最终<span lang="EN-US">,exp里面全部的非终结符号全部变成了终结符号.那么推导完成. 
<p></p></span></span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">这种推导十分像我们在离散数学中讲到的命题推理<span lang="EN-US">.其实形式语言的推导的数学基础就是我们离散数学的命题推理. 
<p></p></span></span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">
								<p>
								</p>
						</span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">在推导过程中<span lang="EN-US">,其实就是把原来的文法中的递归展开.那么我们在推导的过程,也就很容易实现分析树的生成.而分析树就是我们编译程序中十分重要的信息源.我们之所以前面又做词法分析,又做语法分析的目标就是为了生成分析树.有了它,我们编译程序在后面的代码生成过程中将变得容易百倍. 
<p></p></span></span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">
								<p>
								</p>
						</span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">
								<span style="mso-spacerun: yes"> </span>请看: 
<p></p></span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">
								<shapetype id="_x0000_t75" coordsize="21600,21600" o:spt="75" o:preferrelative="t" path="m@4@5l@4@11@9@11@9@5xe" filled="f" stroked="f">
										<stroke joinstyle="miter">
										</stroke>
										<formulas>
												<f eqn="if lineDrawn pixelLineWidth 0">
												</f>
												<f eqn="sum @0 1 0">
												</f>
												<f eqn="sum 0 0 @1">
												</f>
												<f eqn="prod @2 1 2">
												</f>
												<f eqn="prod @3 21600 pixelWidth">
												</f>
												<f eqn="prod @3 21600 pixelHeight">
												</f>
												<f eqn="sum @0 0 1">
												</f>
												<f eqn="prod @6 1 2">
												</f>
												<f eqn="prod @7 21600 pixelWidth">
												</f>
												<f eqn="sum @8 21600 0">
												</f>
												<f eqn="prod @7 21600 pixelHeight">
												</f>
												<f eqn="sum @10 21600 0">
												</f>
										</formulas>
										<path o:extrusionok="f" gradientshapeok="t" o:connecttype="rect">
										</path>
										<lock v:ext="edit" aspectratio="t">
										</lock>
								</shapetype>
								<shape id="_x0000_i1025" style="WIDTH: 414.75pt; HEIGHT: 366.75pt" type="#_x0000_t75">
										<img style="CURSOR: pointer" onclick="javascript:window.open(this.src);" src="http://blog.csdn.net/Develop/ArticleImages/21/21509/CSDN_Dev_Image_2003-10-112125020.jpg" onload="javascript:if(this.width&gt;500)this.style.width=500;" o:title="1" />
								</shape>
								<p>
								</p>
						</span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">
								<p>
								</p>
						</span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span style="FONT-SIZE: 10pt; COLOR: blue; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">例<span lang="EN-US">3.2 
<p></p></span></span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">同样是<span lang="EN-US">&lt;&lt;编译原理及实践&gt;&gt;书上的例子. 
<p></p></span></span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">设<span lang="EN-US">E -&gt; E+a | a 表示的文法为G,那么考虑它生成的表达L(G) 
<p></p></span></span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">如果由标准的数学定义<span lang="EN-US">,那么我们用公式L(G)={s | exp =&gt;* s }表示一种文法G. 
<p></p></span></span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">s代表记号符号的任意数组串,也就是我们的终结符号.而exp代表非终结符号,=&gt;*表示一系列的从非终结符到终结符号的推导过程.这里*有点像我们在讲述正则表达式中的*符号一样,它表示0到无限次的重复.所以=&gt;*就是表示0次到无限次的推导过程. 
<p></p></span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">
								<p>
								</p>
						</span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">L(G) = {a,a+a,a+a+a,a+a+a+a,…} 
<p></p></span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">E =&gt; E+a =&gt; E+a+a =&gt; E+a+a+a 
<p></p></span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">
								<p>
								</p>
						</span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">同时<span lang="EN-US">,在我们的编译课本上,又经常讲述另一种数学表达方式来阐述文法的定义. 
<p></p></span></span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">G=(T,N,P,S) 
<p></p></span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">注意<span lang="EN-US">,这里的T,N,P,S都是集合. 
<p></p></span></span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">T表示终结符号(terminal),也就是这里{a,+} 
<p></p></span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">N表示非终结符号(nonterminal),也就是这里{E},但是N不能与T相交. 
<p></p></span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">P表示产生式(production)或者文法规则(grammar rule)的集合,这里它只有一个元素: E -&gt; E+a 
<p></p></span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">S表示集合N的开始符号(start symbol).关于S,本人也搞不清楚它的用处,所以很抱歉! 
<p></p></span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">
								<p>
								</p>
						</span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">
								<p>
								</p>
						</span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span lang="EN-US" style="FONT-SIZE: 10pt; COLOR: blue; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">
								<p>
								</p>
						</span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span style="FONT-SIZE: 10pt; COLOR: blue; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">例<span lang="EN-US">3.3</span></span>
						<span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">
								<p>
								</p>
						</span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">这是我们<span lang="EN-US">C程序语言中经常使用if else文法 
<p></p></span></span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">statement -&gt; if-stmt | <b>other 
<p></p></b></span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">if-stmt -&gt; <b>if</b><b>(</b>exp<b>)</b> statement |<b> if (</b>exp<b>)</b> statement <b>else</b> statement 
<p></p></span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">exp -&gt; <b>0</b>|<b>1 
<p></p></b></span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<b>
								<span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">
										<p>
										</p>
								</span>
						</b>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">statement就是我们C语言中使用语句,它的产生式包括了两种可能,一是if-stmt语句,二是other.然后我们又另外定义if-stmt语句的产生式.这里有两种情况,一是没有else的,另外就是有else的.里面我们又使用了递归.if-stmt本来是包含在statement里面的,可是我们又在if-stmt的产生式中使用statement.正是因为文法中允许递归,所以它比起我们前面讲的正则表达式有更广泛的表示能力,但同时,文法的推导识别也更加法复杂.按照编译原理的书籍,一般讲完BNF文法后,就要重点讲解文法的推导算法.一共有两种,一是LL算法,自顶向下的算法,二是LR算法,自底向上的算法.LL算法比较简单,其中还有一种特殊的情况,就是我们下一节要讲的递归下降的算法.由于C语言中的函数本来就可以递归,那么实现这中递归下降的算法是十分简单的,而且对于我们一般的程序设计语言来说,虽然它的算法能力很弱,但是已经是足够用了.而关于LR的算法,那么就是一个大难题了.它的算法能力最强,但是实现起来十分困难,还好,已经有科学家为我们提供了yacc(或者叫bison)这个工具,可以来自动生成LR的文法推导算法.这就是我们一直在提到的yacc工具了. 
<p></p></span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">
								<p>
								</p>
						</span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">回过头来<span lang="EN-US">,我们看看下面的程序 
<p></p></span></span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">if(0) other else other 
<p></p></span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">的分析树<span lang="EN-US"><p></p></span></span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">
								<shape id="_x0000_i1026" style="WIDTH: 414.75pt; HEIGHT: 3in" type="#_x0000_t75">
										<img style="CURSOR: pointer" onclick="javascript:window.open(this.src);" src="http://blog.csdn.net/Develop/ArticleImages/21/21509/CSDN_Dev_Image_2003-10-112125022.jpg" onload="javascript:if(this.width&gt;500)this.style.width=500;" o:title="2" />
								</shape>
								<p>
								</p>
						</span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">
								<p>
								</p>
						</span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">
								<span style="mso-spacerun: yes">
								</span>
								<p>
								</p>
						</span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">思考<span lang="EN-US">: 为什么要把文法最终分析成树结构? 
<p></p></span></span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">因为文法本身是递归的<span lang="EN-US">,而表示的递归的最好数据结构就是树,所以我们把文法弄成树结构后,后面在处理代码生成等问题上,也可以用递归来很容易地完成. 
<p></p></span></span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">
								<p>
								</p>
						</span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span style="FONT-SIZE: 10pt; COLOR: blue; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">例<span lang="EN-US">3.4 
<p></p></span></span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">这里我给出<span lang="EN-US">microsoft在msdn中对于C语言的statement的文法 
<p></p></span></span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">注意<span lang="EN-US">,这里用:符号替代了我们前面产生式的-&gt; 
<p></p></span></span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt">
								<p>
								</p>
						</span>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt; TEXT-ALIGN: left; mso-pagination: widow-orphan" align="left">
						<font size="3">
								<i>
										<span lang="EN-US">statement</span>
								</i>
								<span lang="EN-US"> : </span>
								<span lang="EN-US" style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体; mso-font-kerning: 0pt">
										<p>
										</p>
								</span>
						</font>
				</p>
				<p>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt 36pt">
						<i>
								<span lang="EN-US">
										<font size="3">labeled-statement</font>
								</span>
						</i>
						<span lang="EN-US">
								<br />
								<font size="3">
										<i>compound-statement</i>
										<br />
										<i>expression-statement</i>
										<br />
										<i>selection-statement</i>
										<br />
										<i>iteration-statement</i>
										<br />
										<i>jump-statement</i>
										<br />
										<i>try-except-statement</i>   /* Microsoft Specific */<br /><i>try-finally-statement</i>   /* Microsoft Specific */ </font>
						</span>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<font size="3">
								<i>
										<span lang="EN-US">jump-statement</span>
								</i>
								<span lang="EN-US"> : </span>
						</font>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt 36pt">
						<b>
								<span lang="EN-US">
										<font size="3">goto</font>
								</span>
						</b>
						<span lang="EN-US">
								<font size="3">
										<i>identifier</i>
										<b>;</b>
										<br />
										<b>continue;</b>
										<br />
										<b>break;</b>
										<br />
										<b>return</b>
										<i>expression</i>
										<sub>opt</sub>
										<b>;</b>
								</font>
						</span>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<font size="3">
								<i>
										<span lang="EN-US">compound-statement</span>
								</i>
								<span lang="EN-US"> : </span>
						</font>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt 36pt">
						<font size="3">
								<b>
										<span lang="EN-US">{</span>
								</b>
								<span lang="EN-US">
										<i>declaration-list</i>
										<sub>opt</sub>
										<i>statement-list</i>
										<sub>opt</sub>
										<b>}</b>
								</span>
						</font>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<font size="3">
								<i>
										<span lang="EN-US">declaration-list</span>
								</i>
								<span lang="EN-US"> : </span>
						</font>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt 36pt">
						<i>
								<span lang="EN-US">
										<font size="3">declaration</font>
								</span>
						</i>
						<span lang="EN-US">
								<br />
								<font size="3">
										<i>declaration-list declaration</i>
								</font>
						</span>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<font size="3">
								<i>
										<span lang="EN-US">statement-list</span>
								</i>
								<span lang="EN-US"> : </span>
						</font>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt 36pt">
						<i>
								<span lang="EN-US">
										<font size="3">statement</font>
								</span>
						</i>
						<span lang="EN-US">
								<br />
								<font size="3">
										<i>statement-list statement</i>
								</font>
						</span>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<font size="3">
								<i>
										<span lang="EN-US">expression-statement</span>
								</i>
								<span lang="EN-US"> : </span>
						</font>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt 36pt">
						<font size="3">
								<i>
										<span lang="EN-US">expression</span>
								</i>
								<span lang="EN-US">
										<sub>opt</sub>
										<b>;</b>
								</span>
						</font>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<font size="3">
								<i>
										<span lang="EN-US">iteration-statement</span>
								</i>
								<span lang="EN-US"> : </span>
						</font>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt 36pt">
						<b>
								<span lang="EN-US">
										<font size="3">while (</font>
								</span>
						</b>
						<span lang="EN-US">
								<font size="3">
										<i>expression</i>
										<b>)</b>
										<i>statement</i>
										<br />
										<b>do</b>
										<i>statement</i>
										<b>while (</b>
										<i>expression</i>
										<b>);</b>
										<br />
										<b>for (</b>
										<i>expression</i>
										<sub>opt</sub>
										<b>;</b>
										<i>expression</i>
										<sub>opt</sub>
										<b>;</b>
										<i>expression</i>
										<sub>opt</sub>
										<b>)</b>
										<i>statement</i>
								</font>
						</span>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<font size="3">
								<i>
										<span lang="EN-US">selection-statement</span>
								</i>
								<span lang="EN-US"> : </span>
						</font>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt 36pt">
						<b>
								<span lang="EN-US">
										<font size="3">if (</font>
								</span>
						</b>
						<span lang="EN-US">
								<font size="3">
										<i>expression</i>
										<b>)</b>
										<i>statement</i>
										<br />
										<b>if (</b>
										<i>expression</i>
										<b>)</b>
										<i>statement </i>
										<b>else</b>
										<i>statement</i>
										<br />
										<b>switch (</b>
										<i>expression</i>
										<b>)</b>
										<i>statement</i>
								</font>
						</span>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<font size="3">
								<i>
										<span lang="EN-US">labeled-statement</span>
								</i>
								<span lang="EN-US"> : </span>
						</font>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt 36pt">
						<i>
								<span lang="EN-US">
										<font size="3">identifier</font>
								</span>
						</i>
						<span lang="EN-US">
								<font size="3">
										<b>:</b>
										<i>statement</i>
										<br />
										<b>case</b>
										<i>constant-expression</i>
										<b>:</b>
										<i>statement</i>
										<br />
										<b>default :</b>
										<i>statement</i>
								</font>
						</span>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<font size="3">
								<i>
										<span lang="EN-US">try-except-statement</span>
								</i>
								<span lang="EN-US"> :   /* Microsoft Specific */ </span>
						</font>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt 36pt">
						<b>
								<span lang="EN-US">
										<font size="3">__try</font>
								</span>
						</b>
						<span lang="EN-US">
								<font size="3">
										<i>compound-statement</i>
										<br />
										<b>__except</b>
										<b>(</b>
										<i>expression</i>
										<b>)</b>
										<i>compound-statement</i>
								</font>
						</span>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
						<font size="3">
								<i>
										<span lang="EN-US">try-finally-statement</span>
								</i>
								<span lang="EN-US"> :   /* Microsoft Specific */ </span>
						</font>
				</p>
				<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt 36pt">
						<b>
								<span lang="EN-US">
										<font size="3">__try</font>
								</span>
						</b>
						<span lang="EN-US">
								<font size="3">
										<i>compound-statement</i>
										<br />
										<b>__finally</b>
										<i>compound-statement</i>
								</font>
						</span>
				</p>
		</div>
<img src ="http://www.blogjava.net/landy/aggbug/44761.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/landy/" target="_blank">独孤过客</a> 2006-05-06 16:16 <a href="http://www.blogjava.net/landy/archive/2006/05/06/44761.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>从lex&amp;yacc说到编译器(2.flex的使用)</title><link>http://www.blogjava.net/landy/archive/2006/05/06/44760.html</link><dc:creator>独孤过客</dc:creator><author>独孤过客</author><pubDate>Sat, 06 May 2006 08:15:00 GMT</pubDate><guid>http://www.blogjava.net/landy/archive/2006/05/06/44760.html</guid><wfw:comment>http://www.blogjava.net/landy/comments/44760.html</wfw:comment><comments>http://www.blogjava.net/landy/archive/2006/05/06/44760.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/landy/comments/commentRss/44760.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/landy/services/trackbacks/44760.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 看了第一篇的关于正则表达式的说明后,下面我们就来通过它,使用flex这个词法分析工具来构造我们的编译器的词法分析器. 																														关于lex的教程应该是很多,这里我就简单地介绍一下,然后着重后面的lex和yacc的配合使用以及其技巧.所以,如果你不看了后还是不太明白lex或者yacc的使用,请你自己上网去查查,这方面的教程...&nbsp;&nbsp;<a href='http://www.blogjava.net/landy/archive/2006/05/06/44760.html'>阅读全文</a><img src ="http://www.blogjava.net/landy/aggbug/44760.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/landy/" target="_blank">独孤过客</a> 2006-05-06 16:15 <a href="http://www.blogjava.net/landy/archive/2006/05/06/44760.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>从lex&amp;yacc说到编译器(1.正则表达式)</title><link>http://www.blogjava.net/landy/archive/2006/05/06/44759.html</link><dc:creator>独孤过客</dc:creator><author>独孤过客</author><pubDate>Sat, 06 May 2006 08:12:00 GMT</pubDate><guid>http://www.blogjava.net/landy/archive/2006/05/06/44759.html</guid><wfw:comment>http://www.blogjava.net/landy/comments/44759.html</wfw:comment><comments>http://www.blogjava.net/landy/archive/2006/05/06/44759.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/landy/comments/commentRss/44759.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/landy/services/trackbacks/44759.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 学过编译原理的朋友肯定都接触过																		LEX										这个小型的词法扫描工具										. 										但是却很少有人真正把										LEX										用在自己的程序里										. 										在构造专业的编译器的时候										,										常常需...&nbsp;&nbsp;<a href='http://www.blogjava.net/landy/archive/2006/05/06/44759.html'>阅读全文</a><img src ="http://www.blogjava.net/landy/aggbug/44759.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/landy/" target="_blank">独孤过客</a> 2006-05-06 16:12 <a href="http://www.blogjava.net/landy/archive/2006/05/06/44759.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>