欧美性猛交XXXX免费看蜜桃,成人网18免费韩国,亚洲国产成人精品区综合,欧美日韩一区二区三区高清不卡,亚洲综合一区二区精品久久

打開(kāi)APP
userphoto
未登錄

開(kāi)通VIP,暢享免費電子書(shū)等14項超值服

開(kāi)通VIP
javacc簡(jiǎn)介
JavaCC 簡(jiǎn)介
許多基于 Web的項目都包含即席(ad-hoc)查詢(xún)系統以允許終端用戶(hù)搜索信息。因此,終端用戶(hù)會(huì )需要某種語(yǔ)言來(lái)表達他們所希望搜索的內容。以前,用戶(hù)查詢(xún)語(yǔ)言的定義極其簡(jiǎn)單。如果終端用戶(hù)滿(mǎn)足于使用與最典型的 Google 搜索一般簡(jiǎn)單的語(yǔ)言,那么 Java 的 StringTokenizer對于解析任務(wù)就綽綽有余了。然而,如果用戶(hù)希望有一種更健壯的語(yǔ)言,比如要添加括號和"AND"/"OR"邏輯,那么我們很快就會(huì )發(fā)現我們需要更強大的工具。我們需要一種方法,用以首先定義用戶(hù)將要使用的語(yǔ)言,然后用該定義解析相應的條目并且對各種后端數據庫制定正確的查詢(xún)。

這就是工具JavaCC 出現的原因。JavaCC 代表"Java® Compiler Compiler",是對 YACC("Yet AnotherCompiler Compiler")的繼承(YACC 是 AT&T 為了構建 C 和其他高級語(yǔ)言解析器而開(kāi)發(fā)的一個(gè)基于 C的工具)。YACC 和其伙伴詞法記號賦予器(tokenizer)——"Lex"——接收由常用的巴科斯-諾爾范式(Backus-Nauerform,又稱(chēng) Bacchus NormalForm,BNF)形式的語(yǔ)言定義的輸入,并生成一個(gè)"C"程序,用以解析該語(yǔ)言的輸入以及執行其中的功能。JavaCC 與 YACC一樣,是為加快語(yǔ)言解析器邏輯的開(kāi)發(fā)過(guò)程而設計的。但是,YACC 生成 C 代碼,而 JavaCC 呢,正如您想像的那樣,JavaCC 生成的是Java 代碼。

JavaCC 的歷史極具傳奇色彩。它起源于 Sun 公司的"Jack"。Jack后來(lái)輾轉了幾家擁有者,比如著(zhù)名的 Metamata 和 WebGain,最后變成了 JavaCC,然后又回到了 Sun。Sun 公司最后在BSD 的許可下將它作為開(kāi)放源代碼的代碼發(fā)布。JavaCC 目前的 Web 主頁(yè)是 http://javacc.net.java.net。

JavaCC的長(cháng)處在于它的簡(jiǎn)單性和可擴展性。要編譯由 JavaCC 生成的 Java 代碼,無(wú)需任何外部 JAR 文件或目錄。僅僅用基本的 Java1.2 版編譯器就可以進(jìn)行編譯。而該語(yǔ)言的布局也使得它易于添加產(chǎn)生式規則和行為。該 Web站點(diǎn)甚至描述了如何編制異常以便給出用戶(hù)合適的語(yǔ)法提示。

問(wèn)題定義
讓我們假設您有一位客戶(hù)在一個(gè)出租視頻節目的商店里,該商店擁有一個(gè)簡(jiǎn)單的電影數據庫。該數據庫包含表 MOVIES、ACTORS 和KEYWORDS。MOVIES 表列舉他商店中每部電影的相關(guān)數據,即如每部電影的名稱(chēng)和導演等內容。ACTORS 表列舉每部電影中的演員姓名。而KEYWORDS 表則列舉描述電影的詞語(yǔ),例如"action"、"drama"、"adventure"等等。

客戶(hù)希望能夠對該數據庫發(fā)出稍微復雜的查詢(xún)。例如,他想輸入以下形式的查詢(xún)
actor = "Christopher Reeve" and keyword=action and keyword=adventure

并且希望返回由 Christopher Reeve 主演的 Superman 系列電影。他還希望像下面這樣用括號來(lái)說(shuō)明求值次序以區分查詢(xún)
(actor = "Christopher Reeve" and keyword=action) or keyword=romance

這樣可能返回不是由 Christopher Reeve 主演的電影
actor = "Christopher Reeve" and (keyword=action or keyword=romance)

這樣則總會(huì )返回 Christopher Reeve 主演的電影。


解決方案
對于該任務(wù),您將分兩個(gè)階段來(lái)定義解決方案。在第 1 階段中,您將用 JavaCC 定義語(yǔ)言,要確保能夠正確解析終端用戶(hù)的查詢(xún)。在第 2 階段中,您將向 JavaCC 代碼添加行為以產(chǎn)生 DB2® SQL 代碼,從而確保返回正確的電影來(lái)響應終端用戶(hù)的查詢(xún)。

階段 1 - 定義用戶(hù)的查詢(xún)語(yǔ)言
將在名為 UQLParser.jj 的文件里定義該語(yǔ)言。該文件將被 JavaCC 工具編譯成為一組 .java 類(lèi)型的 Java 類(lèi)文件。要在 JJ 文件中定義語(yǔ)言,您需要做以下 5 件事:

定義解析環(huán)境
定義"空白"
定義"標記(token)"
按照標記定義語(yǔ)言本身的語(yǔ)法
定義每個(gè)解析階段中將發(fā)生的行為
您可以通過(guò)所展示的代碼段定義自己的 UQLParser.jj 文件,也可以通過(guò)本文的相關(guān)代碼進(jìn)行效仿。對于步驟 1 到 4,在JavaCCPaper/stage1/src 中使用 UQLParser.jj 的副本。而步驟 5 則在JavaCCPaper/stage2/src 中進(jìn)行。樣本數據庫的 DDL 可以在 JavaCCPaper/moviedb.sql中找到。如果使用相同的用戶(hù)標識創(chuàng )建數據庫和運行解析器,該實(shí)例將運行得最好。Ant 文件(build.xml)可用于加快編譯過(guò)程。

步驟 1. 定義解析環(huán)境

JavaCC.jj 文件通過(guò)執行 JavaCC 將被轉換為 .java 文件。JavaCC 將獲取 .jj 文件里 PARSER_BEGIN 與PARSER_END 的中間部分并將之復制到 Java結果文件中。作為解析器設計者,您可以將解析前后所有與解析器有關(guān)的動(dòng)作置于該文件中。您還可以在其中將 Java 代碼鏈接到步驟 4 和 5中將會(huì )定義的解析器動(dòng)作上。

在以下所示的實(shí)例中,解析器相對比較簡(jiǎn)單。構造函數 UQLParser 接收一個(gè)字符串輸入,通過(guò)Java 的 java.io.StringReader 類(lèi)將其讀入,然后調用另一個(gè)不可見(jiàn)的構造函數將 StringReader 強制轉換為Reader。這里定義的惟一其他方法就是 static main 方法,該方法將在調用構造函數之后再調用迄今還未定義的名為 parse()的方法。

正如您可能已猜到的,JavaCC 已經(jīng)提供了一個(gè) Java Reader 類(lèi)的構造函數。而我們添加了基于字符串的構造函數,以便易于使用和測試。

清單 1. 解析器的 Java 環(huán)境
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)

步驟 2. 定義空白

在該語(yǔ)言中,您希望將空格、跳格、回車(chē)和換行作為分隔符處理,而不是將其忽略。這些字符都被稱(chēng)為 空白 。在 JavaCC 中,我們在 SKIP 區域中定義這些字符,如清單 2 中所示。

清單 2. 在 SKIP 區域中定義空白
/** Skip these characters, they are considered "white space" **/
SKIP :
{
" "
| "\\t"
| "\\r"
| "\\n"

}


步驟 3. 定義標記

接下來(lái),您將定義該語(yǔ)言所識別的標記。 標記 是將對解析程序有意義的解析字符串的最小單位。掃描輸入字符串以及判斷是何標記的過(guò)程稱(chēng)作 記號賦予器(tokenizer) 。在以下查詢(xún)中,

actor = "Christopher Reeve"

其標記為
actor
=
"Christopher Reeve"
在您的語(yǔ)言中,您要將 actor 和等號(=)作為該語(yǔ)言中的保留標記,盡管字 if 和 instanceof 在 Java語(yǔ)言中都是帶有特殊意義的保留標記。通過(guò)保留字和其他特殊標記,程序員希望解析器會(huì )逐字地識別這些字并為其指派特定的意義。如果您正在保留這些字,請繼續進(jìn)行下去并且保留不等號(<>)和左右括號。還要保留名稱(chēng)、導演和關(guān)鍵字以表示用于用戶(hù)搜索的特定字段。

要定義所有這些內容,請使用 JavaCC 的 TOKEN 指令。每個(gè)標記的定義都用尖括號(< 和>)括起來(lái)。在冒號(:)的左邊給出標記的名稱(chēng),并在右邊給出正則表達式。正則表達式是定義將要匹配的文本部分的方式。在其最簡(jiǎn)單的形式中,正則表達式可以匹配精確的字符序列。使用下列代碼來(lái)定義六個(gè)匹配精確字的標記和四個(gè)匹配符號的標記。當分析器看到任何一個(gè)字時(shí),將會(huì )用符號AND、OR、TITLE、ACTOR、DIRECTOR 或 KEYWORD 來(lái)加以匹配。在匹配符號之后,解析器將相應地返回LPAREN、RPAREN、EQUALS 或 NOTEQUAL。清單 3 展示了 JavaCC 保留標記的定義。

清單 3. 定義保留標記
TOKEN: /*RESERVED TOKENS FOR UQL */
{
<AND: "and">
| <OR: "or">
| <TITLE: "title">
| <ACTOR: "actor">
| <DIRECTOR: "director">
| <KEYWORD: "keyword">
| <LPAREN: "(">
| <RPAREN: ")">
| <EQUALS: "=">
| <NOTEQUAL: "<>">
}


對于像"ChristopherReeve"一樣的字符串,您或許無(wú)法在我們的語(yǔ)言中將所有的演員姓名存儲為保留字。但是,您可以通過(guò)使用正則表達式定義的字符模式識別 STRING或 QUOTED_STRING 類(lèi)型的標記。 正則表達式是定義匹配模式的字符串。定義匹配所有字符串或引用字符串的正則表達式要比定義精確的字匹配更具技巧性。

您將定義一個(gè)由一個(gè)或更多字符系列構成的字符串(STRING),其中的有效字符為大小寫(xiě)的 A 到 Z 以及數字 0 到 9。為了簡(jiǎn)單起見(jiàn),不考慮定影明星或電影名稱(chēng)的重音字符或其他不規則體。您可以按下列方式將該模式寫(xiě)為一個(gè)正則表達式。
<STRING : (["A"-"Z", "a"-"z", "0"-"9"])+ >

加號表示圍在括號中的模式(從 A 到 Z、a 到 z 或 0 到 9 中的任何字符)可依次出現一次或多次。在 JavaCC 中,您還可以用星號(*)來(lái)表示模式的零次或多次出現以及用問(wèn)號(?)來(lái)表示 0 或 1 此重復。

QUOTED_STRING就更具技巧性了。如果您定義一個(gè)以引號開(kāi)頭,以引號結尾并在其中包含任何其他字符的字符串,那么該字符串就是一個(gè)QUOTED_STRING。其正則表達式為 "\\"" (~["\\""])+ "\\"",這肯定有些眼花繚亂的。簡(jiǎn)單一點(diǎn)理解就是,由于引用字符本身對于 JavaCC 是有意義的,因此我們需要將對它的引用轉換為對我們的語(yǔ)言而非JavaCC 是有意義的。為了轉換該引用,我們在它之前使用了一個(gè)反斜杠。字符顎化符號(~)意味著(zhù)并非是針對 JavaCC 記號賦予器的。(~["\\""])+ 是對于一個(gè)或更多非引用字符的速寫(xiě)。合在一起, "\\"" (~["\\""])+ "\\""就意味著(zhù)"一個(gè)引用加上一個(gè)或更多非引用再加上一個(gè)引用"。

您必須在保留字規則之后添加 STRING 和QUOTED_STRING的記號賦予器規則。保持該次序是極其重要的,因為記號賦予器規則在文件中出現的次序就是應用標記規則的次序。您需要確定"title"是被視作保留字而非字符串的。清單 4 中顯示了 STRING 和 QUOTED_STRING 標記的完整定義。

清單 4. 定義 STRING 和 QUOTED_STRING
TOKEN :
{
<STRING : (["A"-"Z", "0"-"9"])+ >
<QUOTED_STRING: "\\"" (~["\\""])+ "\\"" >
}


步驟 4. 按照標記定義語(yǔ)言

既然已經(jīng)定義了標記,那么現在是時(shí)候按照標記來(lái)定義解析規則了。用戶(hù)輸入表達式形式的查詢(xún)。一個(gè) 表達式 就是一系列由 布爾運算符 and 或 or 連接的一個(gè)或更多查詢(xún)項。

為了表達這一點(diǎn),我們需要編寫(xiě)一個(gè)解析規則,也稱(chēng)作 產(chǎn)生式 。將清單 5 中的產(chǎn)生式寫(xiě)入 JavaCC UQLParser.JJ 文件。

清單 5. expression() 產(chǎn)生式
void expression() :
{
}
{ queryTerm()
(
( <AND> | <OR> )
queryTerm() )*

}

當對 .jj 文件運行 Javacc 時(shí),產(chǎn)生式將被轉換為方法。所有的 JavaCC產(chǎn)生式方法的返回都必須為空。第一組花括號包含產(chǎn)生式方法所需的所有聲明。這里暫時(shí)為空。第二組花括號包含以 JavaCC理解的方式所寫(xiě)的產(chǎn)生式規則。請注意先前所定義的 AND 和 OR 標記的用法。還請注意,queryTerm()是作為方法調用而寫(xiě)的。實(shí)際上,queryTerm() 是另一個(gè)產(chǎn)生式方法。

現在,就讓我們定義 queryTerm()產(chǎn)生式。queryTerm() 要么是一個(gè)單獨的判別式(例如 title="TheMatrix"),要么是一個(gè)由括號括起來(lái)的表達式。JavaCC 中通過(guò) expression() 遞歸地定義了queryTerm(),這使得您可以通過(guò)清單 6 中所示的代碼簡(jiǎn)明地總結該語(yǔ)言。

清單 6. JavaCC 中的 queryTerm() 產(chǎn)生式方法(UQLParser.jj)
void queryTerm() :
{
}
{
(<TITLE> | <ACTOR> |
<DIRECTOR> | <KEYWORD>)
( <EQUALS> | <NOTEQUAL>)
( <STRING> | <QUOTED_STRING> )
|
<LPAREN> expression() <RPAREN>
}

這就是我們所需的所有規則。兩個(gè)產(chǎn)生式中總結了全部的語(yǔ)言解析器。

將 JavaCC 當作測試驅動(dòng)器
在這個(gè)時(shí)候,您應該已經(jīng)有了一個(gè)有效的 JavaCC 文件。在進(jìn)行到步驟 5 之前,您可以編譯并"運行"該程序以查看您的解析器運作是否正確。

隨本文一起提供的 ZIP 文件應包含了階段 1 的 JavaCC 示例文件 UQLParser.jj。將整個(gè) ZIP文件解壓到一個(gè)空目錄下。要編譯 stage1/UQLParser.jj,您首先需要下載 JavaCC 并根據 JavaCC Web 頁(yè)上的指導進(jìn)行安裝。為了簡(jiǎn)單起見(jiàn),請務(wù)必將 Javacc.bat 的執行路徑填入 PATH 環(huán)境變量中。編譯十分容易,將目錄更改為卸載UQLParser.jj 的位置并輸入下列命令。

javacc "debug_parser " output_directory=.\\com\\demo\\stage1 UQLParser.jj
如果您愿意,也可以使用附帶的 Ant 文件 build.xml。您必須將上方的屬性文件調整為指向 JavaCC 安裝。在您第一次運行它時(shí),JavaCC 將生成如清單 7 中所示的消息。

清單 7. UQLParser.jj 的編譯輸出
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.

除了已提到的四個(gè)文件,JavaCC 還將產(chǎn)生UQLParser.java、UQLParserConstants.java 和UQLParserTokenManager.java。所有這些文件都被寫(xiě)入了 com\\demo\\stage1目錄。此時(shí)起,您就能夠編譯這些文件且無(wú)需向默認的運行時(shí)類(lèi)路徑做任何添加了。如果 JavaCC 步驟運行成功,Ant 文件的默認目標將自動(dòng)執行Java 編譯。如果沒(méi)有成功,您可以用以下命令編譯頂層目錄(JavaCCPaper/stage1)的文件:

javac "d bin src\\com\\demo\\stage1\\*.java
Java 類(lèi)文件一旦就位,您就可以通過(guò)向您所定義的 "main" java 方法輸入下列用戶(hù)示例查詢(xún)來(lái)測試新的解析器了。如果您正使用同一代碼,請從 JavaCCPaper/stage1 目錄開(kāi)始并在命令行中輸入下列命令。

java "cp bin com.demo.stage1.UQLParser "actor = \\"Tom Cruise\\""
我們在 JavaCC 步驟中所使用的 -debug_parser 選項確保將輸出下列有用的跟蹤消息,以顯示用戶(hù)查詢(xún)是如何被解析的。其輸出應該如清單 8 中所示。

清單 8. 查詢(xún) actor="Tom Cruise" 的 UQLParser 輸出
Call: parse
Call: expression
Call: queryTerm
Consumed token: <"actor">
Consumed token: <"=">
Consumed token: <<QUOTED_STRING>:
""Tom Cruise"">
Return: queryTerm
Return: expression
Consumed token: <<EOF>>
Return: parse


要測試帶括號表達式的遞歸路徑,請嘗試以下測試。

java "cp bin com.demo.stage1.UQLParser "(actor=\\"Tom Cruise\\" or actor=\\"Kelly McGillis\\") and keyword=drama"
這將產(chǎn)生清單 9 中的輸出。

清單 9. 查詢(xún) (actor="Tom Cruise" or actor="Kelly McGillis") and keyword=drama 的 UQL1Parser 輸出
Call: parse
Call: expression
Call: queryTerm
Consumed token: <"(">
Call: expression
Call: queryTerm
Consumed token: <"actor">
Consumed token: <"=">
Consumed token: <<QUOTED_STRING>:
""Tom Cruise"">
Return: queryTerm
Consumed token: <"OR">
Call: queryTerm
Consumed token: <"actor">
Consumed token: <"=">
Consumed token: <<QUOTED_STRING>:
""Kelly McGillis"">
Return: queryTerm
Return: expression
Consumed token: <")">
Return: queryTerm
Consumed token: <"AND">
Call: queryTerm
Consumed token: <"keyword">
Consumed token: <"=">
Consumed token: <<STRING>: "drama">
Return: queryTerm
Return: expression
Consumed token: <<EOF>>
Return: parse

該輸出十分有用,因為它演示了通過(guò) queryTerm 和 expression 的遞歸。queryTerm 的第一個(gè)實(shí)例實(shí)際上就是一個(gè)由兩個(gè) queryTerm 組成的表達式。 圖 1展示了該解析路徑的圖形視圖。

圖 1. 解析用戶(hù)查詢(xún)的圖形表示


如果您對于 JavaCC 生成怎樣的 Java 代碼感到好奇,就想盡方法看一看(但不要試圖進(jìn)行任何更改?。?。您將找到以下內容。
UQLParser.java —— 在這一文件中,您將找到您在 UQLParser.jj 文件里的 PARSER_BEGIN 和 PARSER_END 之間所放置的代碼。您還會(huì )發(fā)現 JJ 產(chǎn)生式方法已經(jīng)被改變?yōu)?Java 方法了。

例如,expression() 規則已將被擴展為清單 10 中的代碼了。

清單 10. UQLParser.java
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");
}
}


它與您最初在其中寫(xiě)入 queryTerm()、AND 和 OR 所呈現的樣子有些相像,但其余的就是 JavaCC 所添加的解析細節。

UQLParserConstants.java —— 該文件易于得到。您所定義的所有標記都在這里。JavaCC 只不過(guò)將它們記錄在數組中并提供整型常數來(lái)引用該數組。清單 11 展示了 UQLParserConstants.java 的內容。

清單 11. UQLParserConstants.java
/* 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 = {
"<EOF>",
"\\" \\"",
"\\"\\\\t\\"",
"\\"\\\\r\\"",
"\\"\\\\n\\"",
"\\"and\\"",
"\\"or\\"",
"\\"title\\"",
"\\"actor\\"",
"\\"director\\"",
"\\"keyword\\"",
"\\"(\\"",
"\\")\\"",
"\\"=\\"",
"\\"<>\\"",
"<STRING>",
"<QUOTED_STRING>",
};

}


UQLParserTokenManager.java —— 這是一個(gè)嵌接文件。JavaCC 將該類(lèi)用作記號賦予器。這是一段確定標記為什么的代碼。這里讓人感興趣的首要例程是 GetNextToken。解析器產(chǎn)生式方法將用該例程來(lái)判斷采用哪條路經(jīng)。

SimpleCharStream.java —— UQLParserTokenManager 用該文件來(lái)表示將被解析的字符的 ASCII 流。

Token.java —— 其中提供了 Token 類(lèi)來(lái)表示標記本身。本文的下一部分將演示 Token 類(lèi)的用途。

TokenMgrError.java and ParseException—— 這些類(lèi)分別表示記號賦予器和分析器中的異常狀況。

階段 2 - 給 JavaCC 代碼添加行為
注意:關(guān)于教程的這一部分,請查閱代碼的 stage2 子目錄。從這里開(kāi)始所展示的 JJ 文件就是JavaCCPaper/stage2/UQLParser.jj。為了運行示例 SQL 查詢(xún),您還應該通過(guò)附帶的 moviedb.sql文件創(chuàng )建 MOVIEDB 數據庫。請通過(guò) db2 -tf moviedb.sql 執行 DDL。

既然已經(jīng)進(jìn)行了解析,我們就需要對單個(gè)表達式采取行動(dòng)了。這一階段的目標是生成可運行的 DB2 SQL 查詢(xún)并將返回用戶(hù)期望的結果。

該過(guò)程應該首先從一個(gè)包含空白處的模板 select,解析器將填寫(xiě)此空白處。 清單 12中顯示了 select 模板。解析器所生成的查詢(xún)或許不像人類(lèi) DBA 所寫(xiě)的那樣為最佳的,但是它將返回終端用戶(hù)所期望的正確結果。

清單 12. select 語(yǔ)句
select TITLE, DIRECTOR
FROM MOVIE
where MOVIE_ID IN
(
-- parser will fill in here--
);


解析器填入的內容取決于它通過(guò)記號賦予器所采用的路徑。例如,如果用戶(hù)從上面輸入查詢(xún):

(actor="Tom Cruise" or actor="Kelly McGillis") and keyword=drama"
那么解析器將根據 圖 2 在 SQL 查詢(xún)的遺漏部分中發(fā)出文本。它將回送括號,輸入終端 queryTerm 的子查詢(xún)并且用 INTERSECT 代替 AND 以及 union 代替 OR。

圖 2. SQL 查詢(xún)的解析器輸出


這將確保 SQL 查詢(xún)發(fā)出

(actor = "Tom Cruise" or actor = "Kelly McGillis") and keyword=drama
將如清單 13 中所示。

清單 13. 完整的 select 語(yǔ)句
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‘
);


正如前面提到的,很可能存在更快、更優(yōu)的方法來(lái)編寫(xiě)這個(gè)特定的查詢(xún),但是此 SQL 將生成正確的結果。DB2 優(yōu)化器通??梢越鉀Q性能方面的不足。

因此,需要向 JavaCC 源代碼添加什么來(lái)生成該查詢(xún)呢?您必須添加動(dòng)作和其他支持所定義語(yǔ)法的代碼。 動(dòng)作 是指為響應特定產(chǎn)生式而執行的Java 代碼。在添加動(dòng)作之前,首先要添加將向調用程序返回完整 SQL 的方法。為此,要在 JavaCC 文件的最上部分添加一個(gè)名為getSQL() 的方法。您還應該給解析器的內部成員添加 private StringBuffer sqlSB。該變量將表示任何解析階段的當前SQL 字符串。 清單 14 展示了 UQLParser.jj 的 PARSER_BEGIN/PARSER_END 部分。最后,在main() 測試方法中添加一些代碼,用以輸出和執行所生成的 SQL 查詢(xún)。

清單 14. PARSER_BEGIN/PARSER_END 部分
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)


現在,填入由解析器來(lái)完成的動(dòng)作。我們將先從一個(gè)容易的開(kāi)始。在解析一個(gè)表達式的時(shí)候,解析器每當解析"AND"時(shí)就發(fā)出字"INTERSECT",而解析"OR"時(shí)就發(fā)出"union"。為此,要在 expression 產(chǎn)生式中的<AND> 和 <OR> 標記之后插入自包含的 Java 代碼塊。該代碼應向 sqlSB StringBuffer追加 INTERSECT 或 union。清單 15 中顯示了這些代碼。

清單 15. expression 所執行的動(dòng)作
void expression() :
{
}
{ queryTerm()
(
( <AND>
{ sqlSB.append("\\nINTERSECT\\n"); }
| <OR>
{ sqlSB.append("\\nunion\\n"); }
)
queryTerm() )*
}


queryTerm()


產(chǎn)生式內需要執行多個(gè)動(dòng)作。這些任務(wù)如下:

1.將搜索名稱(chēng)映射到它們各自的 DB2 表和列上
2.保存比較器(comparator)標記
3.將比較字(comparand)轉換為 DB2 可以理解的形式,例如,除去 QUOTED_STRING 標記的雙引號
4.向 sqlSB 發(fā)送合適的子查詢(xún)
5.對于遞歸表達式的情況,隨之發(fā)出括號。
對于所有這些任務(wù),您將需要一些局部變量。如清單 16 中所示,這些變量是在產(chǎn)生式中第一對花括號之間定義的。

清單 16. queryTerm() 的局部變量
void queryTerm() :
{
Token tSearchName, tComparator, tComparand;
String sComparand, table, columnName;
}


第一個(gè)任務(wù)可用清單 17 中的代碼來(lái)完成。設置與所遇標記關(guān)聯(lián)的合適的 DB2 表和列。

清單 17. 將搜索名稱(chēng)映射到 DB2
(
<TITLE> {table = "movie";
columnName = "title"; } |
<DIRECTOR> {table = "movie";
   columnName = "director"; } |
<KEYWORD> {table = "keyword";
columnName = "keyword"; } |
<ACTOR> {table = "actor";
columnName = "name"; }
)


第二個(gè)任務(wù)可用清單 18 中的代碼來(lái)完成。保存標記以便可用于 SQL 緩沖區。

清單 18. 保存比較器
( tComparator=<EQUALS> |
tComparator=<NOTEQUAL> )


第三個(gè)任務(wù)可用清單 19 中的代碼來(lái)完成。相應地設置比較字的值,如果有必要,就從 QUOTED_STRING 標記中除去雙引號。

清單 19. 準備比較字
tComparand=<STRING> {
sComparand = tComparand.image; }
   |
tComparand=<QUOTED_STRING>
{ // need to get rid of quotes.
sComparand =
   tComparand.image.substring(1,
   tComparand.image.length() - 1);
}


第四個(gè)任務(wù)可用清單 20 中的代碼來(lái)完成。完整的查詢(xún)項被追加到了 sql 緩沖區。

清單 20. 編寫(xiě) SQL 表達式
{
sqlSB.append("select MOVIE_ID FROM ").append(table);
sqlSB.append("\\nwhere ").append(columnName);
sqlSB.append(" ").append(tComparator.image);
sqlSB.append(" ‘").append(sComparand).append("‘");
}

最后對于遞歸表達式的情況,當解析器在表達式遞歸中看到括號時(shí),就應該簡(jiǎn)單地進(jìn)行回送,如清單 21 中所示。

清單 21. 回送括號
<LPAREN>
{ sqlSB.append("("); }
expression()
<RPAREN>
{ sqlSB.append(")"); }

清單 22 展示了完整的 queryTerm() 產(chǎn)生式。

清單 22. 完整的 queryTerms() 產(chǎn)生式
/**
* Query terms may consist of a parenthetically
* separated expression or may be a query criteria
* of the form queryName = something or
* queryName <> something.
*
*/
void queryTerm() :
{
Token tSearchName, tComparator, tComparand;
String sComparand, table, columnName;
}
{
(
<TITLE> {table = "movie";
  columnName = "title"; } |
<DIRECTOR> {table = "movie";
  columnName = "director"; } |
<KEYWORD> {table = "keyword";
  columnName = "keyword"; } |
<ACTOR> {table = "actor";
  columnName = "name"; }
)

( tComparator=<EQUALS> |
tComparator=<NOTEQUAL> )

(
tComparand=<STRING>
{ sComparand = tComparand.image; } |
tComparand=<QUOTED_STRING>
{ // 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("‘");
}
|
<LPAREN>
{ sqlSB.append("("); }
expression()
<RPAREN>
{ sqlSB.append(")"); }
}


像前面一樣編譯并運行 UQLParser.jj。訪(fǎng)問(wèn) UQLParser.java并注意產(chǎn)生式規則是如何被整齊地插入生成代碼中的。清單 23 中展示了一個(gè) expression() 方法的擴展實(shí)例。請注意jj_consume_token 調用之后的代碼。

清單 23. UQLParser.java 中的 expression() 方法
/**
* 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");
}
}[/code

按前面一樣運行該代碼。您必須在 CLASSPATH 中包含 db2java.zip。這次,當您運行

java‘cp bin;c:/sqllib/db2java.zip com.demo.stage2.UQLParser "(actor=\\"TomCruise\\" or actor=\\"Kelly McGillis\\") and keyword=drama"
時(shí),它將生成清單 24 中的輸出。

[b]清單 24. 查詢(xún) (actor="Tom Cruise" or actor="Kelly McGillis") and keyword=drama 的 UQL2Parser 輸出 [/b]
[code]Call: parse
Call: expression
Call: queryTerm
Consumed token: <"(">
Call: expression
Call: queryTerm
Consumed token: <"actor">
Consumed token: <"=">
Consumed token: <<QUOTED_STRING>:
""Tom Cruise"">
Return: queryTerm
Consumed token: <"or">
Call: queryTerm
Consumed token: <"actor">
Consumed token: <"=">
Consumed token: <<QUOTED_STRING>:
""Kelly McGillis"">
Return: queryTerm
Return: expression
Consumed token: <")">
Return: queryTerm
Consumed token: <"and">
Call: queryTerm
Consumed token: <"keyword">
Consumed token: <"=">
Consumed token: <<STRING>: "drama">
Return: queryTerm
Return: expression
Consumed token: <<EOF>>
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
      


嘗試更多使用您的解析器的查詢(xún)。試一試使用 NOTEQUAL 標記的查詢(xún),比如actor<>"Harrison Ford"。嘗試一些像"title="一樣的非法查詢(xún),看看將發(fā)生什么情況。通過(guò)非常少的幾行JavaCC 代碼,您就生成了非常有效的終端用戶(hù)查詢(xún)語(yǔ)言。

最后要考慮的問(wèn)題
JavaCC除了提供解析器生成器之外,還提供 JJDOC 工具,用以編制巴科斯-諾爾范式(Bacchus-Nauer Form)表示的語(yǔ)法。JJDOC可以使您易于向終端用戶(hù)提供他們所使用語(yǔ)言的描述。例如,在附帶代碼中提供的 ant 文件有一個(gè)"bnfdoc"目標。

JavaCC還提供名為 JJTree的工具。該工具提供樹(shù)和節點(diǎn)類(lèi),使您易于將代碼分成單離的解析和動(dòng)作類(lèi)。繼續該實(shí)例,您可以考慮為查詢(xún)編寫(xiě)一個(gè)簡(jiǎn)單優(yōu)化器,以消除不必要的INTERSECT 和 union。您可以通過(guò)訪(fǎng)問(wèn)解析樹(shù)的節點(diǎn)以及合并相似的相鄰節點(diǎn)(例如,actor="Tom Cruise" 和actor="Kelly McGillis")來(lái)完成該工作。

JavaCC 擁有一個(gè)豐富的語(yǔ)法庫。您在自己編寫(xiě)解析器之前,一定要查看 JavaCC 的 examples 目錄,以便可能獲取已構建好的解決方案。

請務(wù)必閱讀 JavaCC Web 頁(yè)上的 Frequently Asked Questions 并訪(fǎng)問(wèn) comp.compilers.tools.javacc 上的 javacc 新聞組以便更好地理解 JavaCC 的所有功能和特性。

結束語(yǔ)
JavaCC 是一個(gè)健壯的工具,可用于定義語(yǔ)法并且易于在 Java 商業(yè)應用程序中包含該語(yǔ)法的解析和異常處理。通過(guò)本文,我們說(shuō)明了 JavaCC 可用于為數據庫系統的終端用戶(hù)提供一種功能強大卻很簡(jiǎn)單的查詢(xún)語(yǔ)言。

參考資料
您可以參閱本文在 developerWorks 全球站點(diǎn)上的英文原文.


可在JAVACC網(wǎng)站上找到 JavaCC 程序包。


在Johnson,Stephen C(AT&T Bell Laboratories,Murray Hill,New Jersey07974)所寫(xiě)論文 YACC: Yet Another Compiler Compiler中第一次描述了這個(gè)可廣泛獲得的"編譯器的編譯器"。


一篇由 Oliver Ensileng 撰寫(xiě)的 JavaWorld 的好文章 Build your own Languages with JavaCC。


Jocelyn Paine 的 Introduction to JJTree。


可在 Wikipedia 中找到對于上下文無(wú)關(guān)語(yǔ)法的很好解釋。


關(guān)于作者JoAnnBrereton 是 IBM 的 Software Group,Federal Software Services的一位高級軟件工程師。她已為 IBM 編程近 20 年了。她最近的項目包括為 CBS、Warner Brothers 和 CNN電視網(wǎng)構建視檔案搜索引擎。

[ Edited by flylyke at 2005-05-07 0:32:09 AM ]

http://www.javacc.com/blogview.asp?logID=17

本站僅提供存儲服務(wù),所有內容均由用戶(hù)發(fā)布,如發(fā)現有害或侵權內容,請點(diǎn)擊舉報。
打開(kāi)APP,閱讀全文并永久保存 查看更多類(lèi)似文章
猜你喜歡
類(lèi)似文章
JR 精品文章 - 使用JavaCC做語(yǔ)法分析[轉]
Lucen--基于Java的全文搜索引擎簡(jiǎn)介[轉]
weblucene全文檢索應用實(shí)例研究之---www.bgo.cn
用PMD自動(dòng)執行Java代碼靜態(tài)分析
厚土-浮云: 我的WebLucene安裝經(jīng)驗
《編譯原理簡(jiǎn)明教程》PPT 第13章
更多類(lèi)似文章 >>
生活服務(wù)
分享 收藏 導長(cháng)圖 關(guān)注 下載文章
綁定賬號成功
后續可登錄賬號暢享VIP特權!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服

欧美性猛交XXXX免费看蜜桃,成人网18免费韩国,亚洲国产成人精品区综合,欧美日韩一区二区三区高清不卡,亚洲综合一区二区精品久久