作者:江南白衣 

     ANTLR(ANother Tool for Language Recognition)風頭正盛,經??梢钥吹接盟稣Z法解釋器的項目,比如Hibernate就在3.0換上它來解釋HQL,加強了HQL的語法。
     因為Antlr是EBNF-AST語法解釋系的代表,而自己總是心思思想搞一下DSL(領域語言),所以從Hibernate來學習一下Antlr的應用。

     Hibernate HQL translator作者Joshua Davis的兩個Blog
     Hibernate3 Query Translator Design - Part One : The Basics
     Hibernate3 Query Translator Design - Part Two : Parsing HQL

     Antlr最好的介紹文章是那篇,在《程序員》2004年3月有中文的版本。  不過,那個計算器的例子太簡單了。深刻一點的有。
     另外,SlickEdit 支持Antlr的語法,是一定要用的編輯器,在 ttdown.com上有破解。


  一,Antlr引擎的工作過程大概是這樣的:
  1.Lexer--詞法分析器。 
        定義語言中的各種Token(單詞),如 From 、Where、=、<>.......
        Lexer負責把讀入的普通文本流識別成Token串。

  2.Parser類--語法分析器。
        使用BNF語法,遞歸定義句子的Pattern,如whereStatement、FromStatement、SelectStatement。
     Parser負責把讀入的Token串匹配成句子,翻譯出AST(抽象語法樹)。
     有些簡單的應用,也可以在本層現炒現賣,完成所有動作,屬于Single Pass Builder。

  3.TreeParser--抽象語法樹遍歷器。
        根據Parser類分析出來的AST(抽象語法樹)進行動作。
           
        用Parser把AST抽取出來,再用TreeParser進行動作的Double Pass Builder模式,解耦了Parser和Generation,再配合Template 生成代碼,是Antlr推薦的最佳模式。

  二,開發人員的實際步驟

  1.按照Antlr的簡單語法定義前面講的3個類,文件的后綴名為g。

  2.使用java antlr.Tool xxx.g 命令,把grammar文件編譯成java文件。

  3.編寫應用程序,如:

import antlr.*
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對Antlr的應用

     看過Antlr對HQL的解釋,覺得EBNF系的方法要解釋Java這樣的編程語言還好些,如果要解釋類自然語言的DSL就比較痛苦,所以情緒不是很高漲,挑一條最容易的"Delete from goods where ....." 匆匆走過場

   Joel的一句話對我的影響比較大:"如果為了證明一個微不足道的問題需要花三個小時寫下幾黑板的證明步驟,那么這種機制不可能用來證明任何有趣的東西" 。對于我這個層次的程序員,antlr在我手中造不出有趣的DSL來。

   Hibernate的HQL Grammar文件一共有三個,在/grammar目錄下:
   1.hql.g   定義Token類和Parser類,將HQL解釋成hql的抽象語法樹(AST)
   2.hql-sql.g  定義Tree Walker ,將HQL AST轉化為SQL AST,將生成模塊與Hibernate解耦。
   3.sql-gen.g 定義Tree Walker,從SQL AST生成sql

   下面看 DELETE FROM GOODS的翻譯過程

   1.HqlBaseLexer extends Lexer
     
定義EQ: '=';  LT: '<';  GT: '>';PLUS: '+';等符號
      及IDENT: ( 'a' .. 'z' | '_' ) ( 'a' .. 'z' | '0' .. '9' | '_' | '$' )*

   2.HqlBaseParser extends Parser
      先定義DELETE="delete"; FROM="from"; MIN="min"; 等字符串
      再定義:

statement
          : ( updateStatement 
| deleteStatement | selectStatement )
          ; 三種Statement之一

      deleteStatement
         : DELETE
^
           (optionalFromTokenFromClause)
           (whereClause)
?
         ;DELETE為葉子,(whereClause)可選

      optionalFromTokenFromClause
!
        : (FROM
!)? f:path {
           AST #range 
= #([RANGE, "RANGE"], #f);
           #optionalFromTokenFromClause 
= #([FROM, "FROM"], #range);
          }
         ;不是很好懂對吧,我也這樣覺得,whereClause就更加不要看了。 

     

     3. HqlSqlBaseWalker extends TreeParser  
         hql與sql的delete語句基本上是一樣的,沒什么轉換。

     4.SqlGeneratorBase extends TreeParser
          
         根據SQL AST, 生成SQL語句         

 private StringBuffer buf = new StringBuffer();
          
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 ] ) )
          ;