作者:江南白衣
ANTLR(ANother Tool for Language Recognition)風(fēng)頭正盛,經(jīng)常可以看到用它做語(yǔ)法解釋器的項(xiàng)目,比如Hibernate就在3.0換上它來(lái)解釋HQL,加強(qiáng)了HQL的語(yǔ)法。
因?yàn)锳ntlr是EBNF-AST語(yǔ)法解釋系的代表,而自己總是心思思想搞一下DSL(領(lǐng)域語(yǔ)言),所以從Hibernate來(lái)學(xué)習(xí)一下Antlr的應(yīng)用。
Hibernate HQL translator作者Joshua Davis的兩個(gè)Blog
Hibernate3 Query Translator Design - Part One : The Basics
Hibernate3 Query Translator Design - Part Two : Parsing HQL
Antlr最好的介紹文章是那篇,在《程序員》2004年3月有中文的版本。 不過(guò),那個(gè)計(jì)算器的例子太簡(jiǎn)單了。深刻一點(diǎn)的有
另外,SlickEdit 支持Antlr的語(yǔ)法,是一定要用的編輯器,在 ttdown.com上有破解。
一,Antlr引擎的工作過(guò)程大概是這樣的:
1.Lexer類--詞法分析器。
定義語(yǔ)言中的各種Token(單詞),如 From 、Where、=、<>.......
Lexer負(fù)責(zé)把讀入的普通文本流識(shí)別成Token串。
2.Parser類--語(yǔ)法分析器。
使用BNF語(yǔ)法,遞歸定義句子的Pattern,如whereStatement、FromStatement、SelectStatement。
Parser負(fù)責(zé)把讀入的Token串匹配成句子,翻譯出AST(抽象語(yǔ)法樹(shù))。
有些簡(jiǎn)單的應(yīng)用,也可以在本層現(xiàn)炒現(xiàn)賣,完成所有動(dòng)作,屬于Single Pass Builder。
3.TreeParser類--抽象語(yǔ)法樹(shù)遍歷器。
根據(jù)Parser類分析出來(lái)的AST(抽象語(yǔ)法樹(shù))進(jìn)行動(dòng)作。
用Parser把AST抽取出來(lái),再用TreeParser進(jìn)行動(dòng)作的Double Pass Builder模式,解耦了Parser和Generation,再配合Template 生成代碼,是Antlr推薦的最佳模式。
二,開(kāi)發(fā)人員的實(shí)際步驟
1.按照Antlr的簡(jiǎn)單語(yǔ)法定義前面講的3個(gè)類,文件的后綴名為g。
2.使用java antlr.Tool xxx.g 命令,把grammar文件編譯成java文件。
3.編寫(xiě)應(yīng)用程序,如:
import antlr.collections.*;
public class Main
{
public static void main(String[] args) throws Exception
{
ExprLexer lexer = new ExprLexer(System.in);
ExprParser parser = new ExprParser(lexer);
parser.expr(); AST ast = parser.getAST();
ExprTreeParser treeParser = new ExprTreeParser();
int x = treeParser.expr(ast);
}
}
三,Hibernate對(duì)Antlr的應(yīng)用
看過(guò)Antlr對(duì)HQL的解釋,覺(jué)得EBNF系的方法要解釋Java這樣的編程語(yǔ)言還好些,如果要解釋類自然語(yǔ)言的DSL就比較痛苦,所以情緒不是很高漲,挑一條最容易的"Delete from goods where ....." 匆匆走過(guò)場(chǎng)
Joel的一句話對(duì)我的影響比較大:"如果為了證明一個(gè)微不足道的問(wèn)題需要花三個(gè)小時(shí)寫(xiě)下幾黑板的證明步驟,那么這種機(jī)制不可能用來(lái)證明任何有趣的東西" 。對(duì)于我這個(gè)層次的程序員,antlr在我手中造不出有趣的DSL來(lái)。
Hibernate的HQL Grammar文件一共有三個(gè),在/grammar目錄下:
1.hql.g 定義Token類和Parser類,將HQL解釋成hql的抽象語(yǔ)法樹(shù)(AST)
2.hql-sql.g 定義Tree Walker ,將HQL AST轉(zhuǎn)化為SQL AST,將生成模塊與Hibernate解耦。
3.sql-gen.g 定義Tree Walker,從SQL AST生成sql
下面看 DELETE FROM GOODS的翻譯過(guò)程
1.HqlBaseLexer extends Lexer
定義EQ: '='; LT: '<'; GT: '>';PLUS: '+';等符號(hào)
及IDENT: ( 'a' .. 'z' | '_' ) ( 'a' .. 'z' | '0' .. '9' | '_' | '$' )*
2.HqlBaseParser extends Parser
先定義DELETE="delete"; FROM="from"; MIN="min"; 等字符串
再定義:
: ( updateStatement | deleteStatement | selectStatement )
; 三種Statement之一
deleteStatement
: DELETE^
(optionalFromTokenFromClause)
(whereClause)?
;DELETE為葉子,(whereClause)可選
optionalFromTokenFromClause!
: (FROM!)? f:path {
AST #range = #([RANGE, "RANGE"], #f);
#optionalFromTokenFromClause = #([FROM, "FROM"], #range);
}
;不是很好懂對(duì)吧,我也這樣覺(jué)得,whereClause就更加不要看了。
3. HqlSqlBaseWalker extends TreeParser
hql與sql的delete語(yǔ)句基本上是一樣的,沒(méi)什么轉(zhuǎn)換。
4.SqlGeneratorBase extends TreeParser
根據(jù)SQL AST, 生成SQL語(yǔ)句
protected void out(String s)
{
buf.append(s);
}
statement
: selectStatement | updateStatement | deleteStatement
;
deleteStatement
: #(DELETE { out("delete"); }
from
(whereClause)?
)
;輸出"delete" from
: #(f:FROM { out(" from "); }
(fromTable)* )
fromTable
: #( a:FROM_FRAGMENT { out(a); } (tableJoin [ a ])* { fromFragmentSeparator(a); } )
| #( b:JOIN_FRAGMENT { out(b); } (tableJoin [ b ])* { fromFragmentSeparator(b); } )
; tableJoin [ AST parent ]
: #( c:JOIN_FRAGMENT { out(" "); out(c); } (tableJoin [ c ] )* )
| #( d:FROM_FRAGMENT { nestedFromFragment(d,parent); } (tableJoin [ d ] )* )
;





whereClause
: #(WHERE { out(" where "); } ( conditionList | booleanExpr[ false ] ) )
;
能不能提供完整的sample.
關(guān)于:格式化SQL技術(shù) && 可視化編輯SQL
macroping@sina.com
Thanks.