<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    如鵬網(wǎng) 大學(xué)生計(jì)算機(jī)學(xué)習(xí)社區(qū)

    CowNew開(kāi)源團(tuán)隊(duì)

    http://www.cownew.com 郵件請(qǐng)聯(lián)系 about521 at 163.com

      BlogJava :: 首頁(yè) :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理 ::
      363 隨筆 :: 2 文章 :: 808 評(píng)論 :: 0 Trackbacks

    本章翻譯人 CowNew開(kāi)源團(tuán)隊(duì) 周曉

    記號(hào)流

    長(zhǎng)久以來(lái), 詞法分析器和語(yǔ)法分析器是緊緊耦合在一起的; 也就是說(shuō), 你不可以在他們中間做任何事情,也不能修改記號(hào)流。但是,用記號(hào)流來(lái)處理詞法分析器和語(yǔ)法分析器之間的連接的話,會(huì)給代碼識(shí)別和翻譯帶來(lái)極大的幫助。這個(gè)想法類似于Java的輸入輸出流,利用輸入輸出流你可以用管道的方式處理多個(gè)深加工的數(shù)據(jù)流。

    介紹

    ANTLR識(shí)別任何滿足TokenStream接口的記號(hào)流對(duì)象(2.6以前的版本, 這個(gè)接口叫做Tokenizer);也就是說(shuō)記號(hào)流對(duì)象要實(shí)現(xiàn)以下的方法。

    Token nextToken();

    如圖, 在分析過(guò)程中的某一時(shí)刻,從詞法分析器(生產(chǎn)者)到語(yǔ)法分析器(消費(fèi)者)的一般記號(hào)流會(huì)像是下面的樣子。

    lexer.to.parser.tokens.gif (3585 bytes)

    最普通的記號(hào)流是一個(gè)詞法分析器,但是想象一下,在詞法分析器和語(yǔ)法分析器中間有一個(gè)流的實(shí)體,都能做哪些有趣的事情呢。比如說(shuō),你可以:

    • 過(guò)濾掉不想要的記號(hào)
    • 插入一些輔助的記號(hào),幫助語(yǔ)法分析過(guò)程識(shí)別一些復(fù)雜的結(jié)構(gòu)
    • 把一個(gè)流分成多個(gè)流,把某些感興趣的記號(hào)送到不同的流中
    • 把多個(gè)記號(hào)流合并成一個(gè)流,從而“模擬”PCCTS,lex等詞法分析工具的狀態(tài)。

    記號(hào)流概念的優(yōu)雅在于它對(duì)詞法分析器和語(yǔ)法分析器沒(méi)有影響--它們只不過(guò)是流的生產(chǎn)者和消費(fèi)者。流對(duì)象是消費(fèi)者用來(lái)生產(chǎn),處理,合并或者分離記號(hào)流的過(guò)濾器。可以使已有的詞法分析器和語(yǔ)法分析器在不修改的情況下合并成一種新的工具。

    這份文檔正式提出了記號(hào)流的概念,詳細(xì)描述了一些非常有用的流過(guò)濾器。

    讓記號(hào)通過(guò)的記號(hào)流

    一個(gè)記號(hào)流是任何滿足下面接口的對(duì)象。

    public interface TokenStream {
    public Token nextToken()
        throws java.io.IOException;
    }

    例如, 一個(gè)"沒(méi)有操作"或者說(shuō)僅僅是讓記號(hào)通過(guò)這里的過(guò)濾器是這樣工作的:

    import antlr.*;
    import java.io.IOException;
    class TokenStreamPassThrough
        implements TokenStream {
    protected TokenStream input;
    /** Stream to read tokens from */
      public TokenStreamPassThrough(TokenStream in) {
    input = in;
    }
    /** This makes us a stream */
      public Token nextToken() throws IOException {
    return input.nextToken(); // "short circuit"
    }
    }

    在下面的main()程序中,你使用這個(gè)簡(jiǎn)單的流對(duì)象從詞法分析器中獲得記號(hào),然后語(yǔ)法分析器從流對(duì)象中獲得記號(hào)。

    public static void main(String[] args) {
      MyLexer lexer =
    new MyLexer(new DataInputStream(System.in));
    TokenStreamPassThrough filter =
        new TokenStreamPassThrough(lexer);
    MyParser parser = new MyParser(filter);
      parser.startRule();
    }

    記號(hào)流過(guò)濾

    多數(shù)情況下,你希望詞法分析器忽略掉空白符和注釋,然而,你還希望在語(yǔ)法分析器必須使用注釋的情況下重用詞法分析器。這時(shí),你只要為需要注釋和空白符連同普通記號(hào)的情況設(shè)計(jì)一個(gè)詞法分析器。然后,當(dāng)你想忽略空白符的時(shí)候,只要在詞法分析器和語(yǔ)法分析器中間加入一個(gè)過(guò)濾器,過(guò)濾掉空白符。

    針對(duì)這種情況,ANTLR提供了TokenStreamBasicFilter。你可以在不修改詞法分析器的情況下讓它過(guò)過(guò)濾掉任何類型的記號(hào)。下面TokenStreamBasicFilter的用法的例子中過(guò)濾掉了注釋和空白符。

    public static void main(String[] args) {
      MyLexer lexer =
    new MyLexer(new DataInputStream(System.in));
    TokenStreamPassThrough filter =
        new TokenStreamPassThrough(lexer);
    filter.discard(MyParser.WS);
    filter.discard(MyParser.COMMENT);
      MyParser parser = new MyParser(filter);
      parser.startRule();
    }

    可以看到,它比修改詞法分析器的詞法結(jié)構(gòu)要來(lái)的有效,你也會(huì)這么做的吧,因?yàn)檫@樣你不用去構(gòu)建一個(gè)記號(hào)對(duì)象。另一方面,采用這種過(guò)濾流的方法使詞法分析器的設(shè)計(jì)更加靈活。

    記號(hào)流分離

    有時(shí),在識(shí)別階段,你想讓翻譯器忽略但不是丟棄輸入的部分記號(hào)。比如說(shuō),你想在語(yǔ)法分析時(shí)忽略注釋,但在翻譯時(shí)又需要注釋。解決辦法是將注釋發(fā)送到一個(gè)隱藏的記號(hào)流中,所謂隱藏,就是語(yǔ)法分析器沒(méi)有對(duì)它進(jìn)行監(jiān)聽(tīng)。在識(shí)別期間,通過(guò)動(dòng)作來(lái)檢查這些隱藏的流,收集注釋等等。流分離過(guò)濾器有點(diǎn)像棱鏡把白光分離成彩虹。

    下面的圖中示出了把一個(gè)記號(hào)流分成三個(gè)的情況。

    stream.splitter.gif (5527 bytes)

    讓語(yǔ)法分析器從最上面的流中獲得記號(hào)。

    用流分離器可以實(shí)現(xiàn)很多功能。比如,"Y-分離器"像有線電視Y連接器一樣,復(fù)制符號(hào)流。如果過(guò)濾器是線程安全的而且有緩沖器緩沖,過(guò)濾器就可以同時(shí)為多個(gè)語(yǔ)法分析器提供記號(hào)。

    這一節(jié)描述了ANTLR提供的一個(gè)叫做TokenStreamHiddenTokenFilter的流過(guò)濾器,它工作的時(shí)候有點(diǎn)像給一堆硬幣分類,把一分一分的放到一個(gè)箱子里,把一角一角的放到另一個(gè)箱子里,等等。這個(gè)過(guò)濾器把輸入流分離成兩個(gè)流,一個(gè)包含主要記號(hào),另一個(gè)被緩沖以便以后可以訪問(wèn)。因?yàn)檫@種實(shí)現(xiàn)方式,無(wú)論怎么做,你都無(wú)法讓語(yǔ)法分析器直接訪問(wèn)隱藏流。下面你將會(huì)看到,過(guò)濾器實(shí)際上把隱藏記號(hào)交織在主記號(hào)中。

    例子

    考慮以下的簡(jiǎn)單文法,該文法用來(lái)聲明整型變量.

    decls: (decl)+
    ;
    decl : begin:INT ID end:SEMI
    ; 

    比如說(shuō)有以下輸入:

    int n; // list length
    /** doc */
    int f;

    假定詞法分析器忽略空白符,你用過(guò)濾器把注釋分離到一個(gè)隱藏流。那么現(xiàn)在如果語(yǔ)法分析器從主記號(hào)流中獲得記號(hào),它只會(huì)看到"INT ID SEMI FLOAT ID SEMI",注釋在隱藏流中。實(shí)現(xiàn)了語(yǔ)法分析器可以忽略注釋,而你的語(yǔ)義動(dòng)作可以從過(guò)濾器中查詢隱藏流中的記號(hào)。

    第一次識(shí)別出文法規(guī)則decl,begin記號(hào)前后沒(méi)有對(duì)隱藏記號(hào)的引用,但

    filter.getHiddenAfter(end)

    返回一個(gè)對(duì)下面記號(hào)的引用

    // list length

    接下來(lái)就會(huì)訪問(wèn)到

    /** doc */

    第二次識(shí)別出decl

    filter.getHiddenBefore(begin)

    指向

    /** doc */

    的引用

    過(guò)濾器實(shí)現(xiàn)

    下圖示出記號(hào)對(duì)象是如何組織記號(hào)來(lái)模擬兩個(gè)不同的流。

    hidden.stream.gif (3667 bytes)

     

    因?yàn)橛浱?hào)是假定的,TokenStreamHiddenTokenFilter對(duì)象通過(guò)鏈表來(lái)連接隱藏記號(hào)和主記號(hào)。過(guò)濾器只提供了一個(gè)物理上的記號(hào)流,通過(guò)交織指針維護(hù)和管理記號(hào)次序。

    因?yàn)檫@個(gè)額外的指針需要把記號(hào)連接到一起,你必須要用一個(gè)叫CommonHiddenStreamToken的特殊記號(hào)對(duì)象(普通記號(hào)對(duì)象叫做CommonToken)。前面曾說(shuō)過(guò),你可以用以下的方法去指定詞法分析器為特定的類制造記號(hào)。

    lexer.setTokenObjectClass("classname");

    技術(shù)上,如果不請(qǐng)求特殊記號(hào)對(duì)象,也可以實(shí)現(xiàn)同樣功能的過(guò)濾器,但這樣實(shí)現(xiàn)非常有效率而且它很容易告訴詞法分析器去生成什么種類的記號(hào)。進(jìn)一步說(shuō),這樣實(shí)現(xiàn)使得它很容易去自動(dòng)完成包含隱藏流信息的樹(shù)結(jié)點(diǎn)的創(chuàng)建。

    這個(gè)過(guò)濾器影響ANTLR的懶惰消費(fèi)。在識(shí)別每一個(gè)主記號(hào)之后, TokenStreamHiddenTokenFilter必須獲取下一個(gè)記號(hào)看它是不是一個(gè)隱藏記號(hào)。因此,這個(gè)過(guò)濾器在交互程序(比如命令行)下工作的不是很好。

    怎樣使用這個(gè)過(guò)濾器

    要使用TokenStreamHiddenTokenFilter,僅僅要做的是:

    • 創(chuàng)建詞法分析器,讓它創(chuàng)建鏈接隱藏記號(hào)的記號(hào)對(duì)象。
    MyLexer lexer = new MyLexer(some-input-stream);
    lexer.setTokenObjectClass(
      "antlr.CommonHiddenStreamToken"
    );
    • 創(chuàng)建一個(gè)TokenStreamHiddenTokenFilter對(duì)象,該對(duì)象從上面的詞法分析器中獲得記號(hào)。
    TokenStreamHiddenTokenFilter filter =
      new TokenStreamHiddenTokenFilter(lexer);
    • 設(shè)置TokenStreamHiddenTokenFilter要隱藏哪些記號(hào),要丟棄哪些記號(hào)。例如,
    filter.discard(MyParser.WS);
    filter.hide(MyParser.SL_COMMENT);
    • 創(chuàng)建一個(gè)語(yǔ)法分析器,從TokenStreamHiddenTokenFilter而不是從詞法分析器中獲得記號(hào)。
    MyParser parser = new MyParser(filter);
    try {
    parser.startRule(); // parse as usual
    }
    catch (Exception e) {
    System.err.println(e.getMessage());
    }

    查看ANTLR指南,在preserving whitespace處有一個(gè)完整的例子。

    樹(shù)的構(gòu)建

    最后,在翻譯階段,如果需要這些隱藏的流記號(hào),在遍歷樹(shù)的時(shí)候,則可以按正常的方式使用。怎么做才能在不打亂樹(shù)文法的情況下把隱藏流的信息送給翻譯器呢?很簡(jiǎn)單: 用AST結(jié)點(diǎn)儲(chǔ)存這些隱藏流記號(hào)。ANTLR定義了CommonASTWithHiddenTokens來(lái)自動(dòng)連接隱藏流記號(hào)到樹(shù)結(jié)點(diǎn); 聯(lián)合一個(gè)樹(shù)結(jié)點(diǎn),適用于樹(shù)結(jié)點(diǎn)的方法在這里也適用于訪問(wèn)這些隱藏記號(hào)。你只需要告訴語(yǔ)法分析器去創(chuàng)建這種類型的結(jié)點(diǎn)而不是默認(rèn)的CommonAST類型的結(jié)點(diǎn)。

    parser.setASTNodeClass("antlr.CommonASTWithHiddenTokens");

    樹(shù)結(jié)點(diǎn)作為記號(hào)對(duì)象的功能被創(chuàng)建。當(dāng)ASTFactory創(chuàng)建樹(shù)結(jié)點(diǎn)的時(shí)候,樹(shù)結(jié)點(diǎn)的initialize()方法被調(diào)用。從記號(hào)創(chuàng)建的樹(shù)結(jié)點(diǎn)包含隱藏記號(hào),不管在前還是在后,都有著同樣的對(duì)隱藏記號(hào)的引用。你不需要用這個(gè)結(jié)點(diǎn)定義,但它工作在很多翻譯任務(wù)中:

    package antlr;
    /** CommonAST在初始化時(shí)把從記號(hào)中獲得
    *  的隱藏記號(hào)的信息復(fù)制,用來(lái)創(chuàng)建結(jié)點(diǎn)
    */
    public class CommonASTWithHiddenTokens
    extends CommonAST {
    // 指向隱藏記號(hào)
    protected Token hiddenBefore, hiddenAfter;
    public CommonHiddenStreamToken getHiddenAfter() {
        return hiddenAfter;
      }
    public CommonHiddenStreamToken getHiddenBefore() {
        return hiddenBefore;
      }
    public void initialize(Token tok) {
    CommonHiddenStreamToken t =
          (CommonHiddenStreamToken)tok;
    super.initialize(t);
    hiddenBefore = t.getHiddenBefore();
    hiddenAfter  = t.getHiddenAfter();
    }
    }

    注意到這個(gè)結(jié)點(diǎn)定義假設(shè)你用了CommonHiddenStreamToken對(duì)象。如果你沒(méi)有讓詞法分析器創(chuàng)建CommonHiddenStreamToken對(duì)象,就會(huì)出現(xiàn)運(yùn)行時(shí)類下行異常。

    垃圾回收問(wèn)題

    利用對(duì)主流記號(hào)的引用分隔輸入流,保存隱藏流記號(hào)時(shí),GC允許在記號(hào)流上工作。在上面的整數(shù)聲明的例子中,當(dāng)沒(méi)有更多的對(duì)第一個(gè)SEMI記號(hào)以及第二個(gè)INT記號(hào)的引用時(shí),comment記號(hào)將會(huì)作為垃圾回收的候選人。如果所有的記號(hào)是連在一起的,一個(gè)單獨(dú)的對(duì)任意記號(hào)的引用會(huì)阻止任何記號(hào)被回收。這在ANTLR實(shí)現(xiàn)不是問(wèn)題。

    提示

    翻譯時(shí),過(guò)濾器在保存空白符和注釋方面做得很好,但在處理輸出和輸入相差很遠(yuǎn)的情況下,用過(guò)濾器不是一個(gè)最好的辦法。例如,有3個(gè)注釋散布在一個(gè)輸入聲明語(yǔ)句中,你想在翻譯階段合并到輸出聲明語(yǔ)句的頭部。比起察看注釋周圍的每一個(gè)分析的記號(hào),更好的辦法是有一個(gè)真正實(shí)際上分開(kāi)的流來(lái)保存注釋以及一個(gè)方法去聯(lián)系分析好的記號(hào)的組和注釋流記號(hào)的組。你或許想支持像"給我在注釋流上從開(kāi)始分析到結(jié)束分析時(shí)最初出現(xiàn)的的所有記號(hào)."

    這個(gè)過(guò)濾器實(shí)現(xiàn)了同JavaCC中special記號(hào)及其相似的功能。Sriram Sankar (JavaCC之父) 關(guān)于特殊記號(hào)有一個(gè)非常好的想法,在1997的Dr. T's Traveling Parsing Revival and Beer Tasting Festival,出席者認(rèn)同了泛化記號(hào)流的概念。現(xiàn)在JavaCC特殊記號(hào)功能僅僅是另一個(gè)ANTLR流過(guò)濾器,好處是你不用去為指定哪些記號(hào)是特殊的而修改詞法分析器。

    記號(hào)流多路技術(shù) (又叫 "詞法分析器多狀態(tài)")

    現(xiàn)在,考慮一下相反的問(wèn)題,你想合并多個(gè)流而不是把一個(gè)流分成多個(gè)。當(dāng)你的輸入包含根本上不同的片段,比如說(shuō)Java和JavaDoc注釋,你會(huì)發(fā)現(xiàn)僅用一個(gè)詞法分析器去識(shí)別所有的輸入片段是很難的。這主要是因?yàn)楹喜⒏鞣N部分的記號(hào)定義會(huì)造成二義性詞法語(yǔ)言或者識(shí)別出一些錯(cuò)誤的記號(hào)。例如,"final"在某些部分里是一個(gè)關(guān)鍵字,但在另一個(gè)部分里它可能會(huì)是一個(gè)標(biāo)示符。同樣,"@author"是一個(gè)合法的javadoc注釋里的標(biāo)記,但在Java代碼中,它是不合法的。

    大部分人為了解決這個(gè)問(wèn)題,為詞法分析器設(shè)定了很多狀態(tài),在不同的部分里切換到不同的狀態(tài)來(lái)工作(例如,在"讀取Java模式"和"讀取JavaDoc模式"中間切換)。詞法分析器開(kāi)始是以Java模式工作的,然后在遇到"/**"后切換到JavaDoc模式; "*/"強(qiáng)制切換回Java模式。

    多詞法分析器

    讓一個(gè)詞法分析器在多個(gè)狀態(tài)下工作可以解決上述的問(wèn)題,但有一個(gè)更好的解決問(wèn)題的辦法,讓多個(gè)詞法分析器協(xié)同工作,生成一個(gè)記號(hào)流。為什么說(shuō)這種辦法更好呢?因?yàn)榉珠_(kāi)的詞法分析器更利于重用(不是剪貼粘貼,而僅僅是告訴流的多元管理器去切換不同的詞法分析器,就得到了一個(gè)新的詞法分析器)。例如,JavaDoc詞法分析器可以在解決任何有JavaDoc注釋的語(yǔ)言問(wèn)題時(shí)得到重用。

    ANTLR預(yù)設(shè)了一個(gè)記號(hào)流叫做TokenStreamSelector,可以用它在多個(gè)詞法分析器間進(jìn)行切換。不同詞法分析器中定義的動(dòng)作控制這個(gè)選擇器切換輸入流??紤]下面的Java代碼片段。

    /** Test.
    *  @author Terence
    */
    int n;

    給出2個(gè)詞法分析器,JavaLexer和JavaDocLexer,2個(gè)詞法分析器的動(dòng)作序列可能看起來(lái)是下面的樣子:

    JavaLexer: 匹配JAVADOC_OPEN, 切換到JavaDocLexer
    JavaDocLexer: 匹配AUTHOR
    JavaDocLexer: 匹配ID
    JavaDocLexer: 匹配JAVADOC_CLOSE, 切換回JavaLexer
    JavaLexer: 匹配INT
    JavaLexer: 匹配ID
    JavaLexer: 匹配SEMI

    在Java詞法分析器的文法中,你需要定義一個(gè)規(guī)則去切換到JavaDoc詞法分析器(把需要切換的詞法分析器記錄在棧中):

    JAVADOC_OPEN
    :    "/**" {selector.push("doclexer");}
    ;

    同樣地,在JavaDoc詞法分析器中定義一個(gè)規(guī)則切換回去:

    JAVADOC_CLOSE
    :    "*/" {selector.pop();}
    ;

    選擇器中有一個(gè)棧,所以JavaDoc詞法分析器不用知道誰(shuí)調(diào)用了它。

    入圖,選擇器和并2個(gè)詞法分析器流,為下游的語(yǔ)法分析器提供單獨(dú)的一個(gè)記號(hào)流。

    stream.selector.gif (5976 bytes)

    選擇器可以為你維護(hù)流列表,所以你可以通過(guò)名字或者實(shí)際對(duì)象的引用來(lái)切換到另一個(gè)輸入流。

    public class TokenStreamSelector implements TokenStream {
    public TokenStreamSelector() {...}
    public void addInputStream(TokenStream stream,
        String key) {...}
    public void pop() {...}
    public void push(TokenStream stream) {...}
    public void push(String sname) {...}
    /** Set the stream without pushing old stream */
    public void select(TokenStream stream) {...}
    public void select(String sname)
        throws IllegalArgumentException {...}
    }

    用選擇器很簡(jiǎn)單:

    • 創(chuàng)建一個(gè)選擇器。
    TokenStreamSelector selector =
    new TokenStreamSelector();
    • 為流命名(不是一定要命名--在切換的時(shí)候你可以用流對(duì)象的引用來(lái)避免使用哈希表查找)。
    selector.addInputStream(mainLexer, "main");
    selector.addInputStream(doclexer, "doclexer");
    • 選擇哪一個(gè)詞法分析器先從字符流中讀數(shù)據(jù)。
    // start with main java lexer
    selector.select("main");
    • 語(yǔ)法分析器可以像用詞法分析器一樣使用選擇器。
    JavaParser parser = new JavaParser(selector);

    詞法分析器共享同一字符流

    在介紹語(yǔ)法分析器如何使用選擇器之前,注意一下這2個(gè)詞法分析器都要從同一個(gè)輸入流中讀取字符。以前的ANTLR2.6.0版本,每一個(gè)詞法分析器都有它自己的記錄行號(hào)的變量,輸入字符流變量等等。為了共享同樣的輸入狀態(tài),ANTLR2.6.0代理詞法分析器來(lái)處理字符輸入到對(duì)象中,LexerSharedInputState可以被n個(gè)詞法分析器共享(單線程)。為了讓多個(gè)詞法分析器共享狀態(tài),你創(chuàng)建第一個(gè)詞法分析器,獲得它的輸入狀態(tài)對(duì)象,然后在構(gòu)建其它詞法分析器并且需要共享輸入狀態(tài)的時(shí)候使用它:

    // 創(chuàng)建Java詞法分析器
    JavaLexer mainLexer = new JavaLexer(input);
    // 創(chuàng)建javadoc詞法分析器; 使用
    // java詞法分析器的共享輸入狀態(tài)
    JavaDocLexer doclexer =
      new JavaDocLexer(mainLexer.getInputState());

    分析多元記號(hào)流

    就像一個(gè)詞法分析器在從不同的輸入片段中生產(chǎn)一個(gè)單獨(dú)的流會(huì)遇到很多麻煩,一個(gè)單獨(dú)的語(yǔ)法分析器在處理多記號(hào)流的時(shí)候也會(huì)遇到一些麻煩。又是同樣的問(wèn)題,一個(gè)記號(hào)在一個(gè)詞法分析器中可能是一個(gè)關(guān)鍵字,在另一個(gè)詞法分析器中可能會(huì)是一個(gè)標(biāo)示符。把語(yǔ)法分析器分解成子分析器,為每一個(gè)輸入片段單獨(dú)處理它們的單詞表,這樣做同時(shí)也利于語(yǔ)法分析器的重用。

    下面的語(yǔ)法分析器文法用主詞法記號(hào)的詞表(用importVocab指定),在遇到JAVADOC_OPEN的時(shí)候,它創(chuàng)建并且調(diào)用一個(gè)JavaDoc分析器去處理接下來(lái)在注釋中的記號(hào)流。

    class JavaParser extends Parser;
    options {
    importVocab=Java;
    }
    input
    :   ( (javadoc)? INT ID SEMI )+
    ;
    javadoc
    :   JAVADOC_OPEN
    {
            // 創(chuàng)建一個(gè)分析器去處理javadoc注釋
    JavaDocParser jdocparser =
              new JavaDocParser(getInputState());
    jdocparser.content(); // 用jdocparser繼續(xù)分析
    }
            JAVADOC_CLOSE
    ;

    你會(huì)發(fā)現(xiàn)從2.6.0版本起,ANTLR語(yǔ)法分析器也共享記號(hào)輸入流狀態(tài)。當(dāng)創(chuàng)建"子分析器"時(shí), JavaParser告訴它從同一輸入狀態(tài)對(duì)象中獲取記號(hào)。

    JavaDoc分析器匹配一串標(biāo)簽:

    class JavaDocParser extends Parser;
    options {
    importVocab=JavaDoc;
    }
    content
    :   (   PARAM // includes ID as part of PARAM
    |   EXCEPTION
            |   AUTHOR
    )*
    ;

    當(dāng)子分析器規(guī)則content結(jié)束后,控制權(quán)自然地返回給它的調(diào)用方,也就是Java分析器中的javadoc。

    多記號(hào)流超前掃描的作用

    如果語(yǔ)法分析器需要去查看JavaDOc注釋起始位置后的2個(gè)記號(hào),那會(huì)發(fā)生什么呢?換句話說(shuō),以主分析器來(lái)看,JAVADOC_OPEN之后的記號(hào)是什么呢? 自然是記號(hào) JAVADOC_CLOSE! 主分析器把任何JavaDoc注釋都看作是一個(gè)實(shí)體,不管這個(gè)注釋有多復(fù)雜; 它不用去了解注釋記號(hào)流內(nèi)部情況,它也不需要這么做--子分析器會(huì)去處理這件事情。

    在子分析器中,content規(guī)則后是什么記號(hào)呢?是"End of file"記號(hào)。子分析器的分析過(guò)程不能確定你的代碼中將會(huì)調(diào)用怎樣的方法。但這不是一個(gè)問(wèn)題,因?yàn)橐话闱闆r會(huì)有一個(gè)單獨(dú)的記號(hào)標(biāo)示子分析過(guò)程的結(jié)束。即使因?yàn)槟撤N原因EOF被載入到分析過(guò)程,EOF也不會(huì)出現(xiàn)在記號(hào)流中。

    多詞法分析器vs調(diào)用另一條詞法規(guī)則

    多詞法分析器狀態(tài)也經(jīng)常被用來(lái)處理那些非常復(fù)雜的單個(gè)記號(hào),比如說(shuō)嵌入轉(zhuǎn)義字符的字符串,輸入"\t"應(yīng)該被識(shí)別為一個(gè)字符串。針對(duì)這種情況,典型的做法是在第一個(gè)引號(hào)之后,切換詞法分析器到"字符串狀態(tài)"然后在識(shí)別完字符串之后切換回"普通狀態(tài)"。

    所以把這種代碼依靠模式做事情的方式叫做"模態(tài)"編程,這通常是一個(gè)比較差的方式。在這種復(fù)雜記號(hào)的情況下,最好是用多個(gè)規(guī)則直接指定復(fù)雜的記號(hào)。這里有一個(gè)什么時(shí)候該用多記號(hào)流和什么時(shí)候不該用的黃金規(guī)則:

    復(fù)雜的單個(gè)記號(hào)應(yīng)該通過(guò)調(diào)用另一個(gè)(protected)詞法規(guī)則來(lái)匹配,對(duì)一段輸入片段來(lái)說(shuō)應(yīng)該用多個(gè)詞法分析器處理此輸入流并提供給分析器。

    例如,詞法分析器字符串的定義應(yīng)該僅僅是調(diào)用另一個(gè)規(guī)則來(lái)處理轉(zhuǎn)義字符的情況:

    STRING_LITERAL
    :    '"' (ESC|~('"'|'\\'))* '"'
    ;
    protected // 不是一個(gè)記號(hào); 僅僅被另一個(gè)規(guī)則調(diào)用
    ESC
    :    '\\'
    (    'n'
    |    'r'
    |    't'
    |    'b'
    |    'f'
    |    '"'
    |    '\''
    |    '\\'
    |    ('u')+
                 HEX_DIGIT HEX_DIGIT HEX_DIGIT HEX_DIGIT
    ...
    )
        ;

    TokenStreamRewriteEngine 簡(jiǎn)單的語(yǔ)法制導(dǎo)翻譯

    在很多情況下,你希望在原代碼基礎(chǔ)上修改或者增加一段程序或數(shù)據(jù)文件。ANTLR 2.7.3 介紹了一個(gè)(只有Java/C#版本)非常簡(jiǎn)單但非常強(qiáng)大的類TokenStream處理以下問(wèn)題:
    1. 輸出語(yǔ)言和輸入語(yǔ)言相似
    2. 語(yǔ)言元素的相對(duì)次序不改變
    參見(jiàn)antlr網(wǎng)站上的
    Syntax Directed TokenStream Rewriting。

    將來(lái)

    ANTLR 2.6版本為使用記號(hào)流提供了基本結(jié)構(gòu),一旦我們有經(jīng)驗(yàn)使用它們,今后的版本將會(huì)更加強(qiáng)大。

    現(xiàn)在的"隱藏記號(hào)"流過(guò)濾器解決"忽略但保存空白符"的問(wèn)題解決的很好,但它在很多情況下不能很好的處理注釋。例如,在真正的翻譯過(guò)程中,你想在各種單獨(dú)的樹(shù)結(jié)點(diǎn)上收集注釋(像DECL或者M(jìn)ETHOD)而不是把它們分布在樹(shù)的各個(gè)地方。你迫切需要一個(gè)流分離器緩沖注釋到一個(gè)隔離的流中,這時(shí)你就可以說(shuō)"給我在識(shí)別這個(gè)規(guī)則上所用掉的所有注釋"或者 "給我這2個(gè)記號(hào)之間的所有注釋." 這幾乎是你在注釋翻譯時(shí)所需要的所有功能。

    記號(hào)流會(huì)帶來(lái)很多便利。大部分人不經(jīng)常思考關(guān)于記號(hào)流的事,使得很難去想象可以在其他一些什么地方可以變的更好。讓思想更奔放一些。怎樣處理嵌入語(yǔ)言的輸入片段,就像你所能看到的Java中嵌入SQL(每一個(gè)輸入的部分被分開(kāi)并通過(guò)不同的流)。怎么樣分析含有和不含有調(diào)試信息的Java .class文件?如果你有一個(gè)可以分析不含調(diào)試信息的.class文件分析器,你想用這個(gè)來(lái)分析含有調(diào)試信息的.class文件,不要去管這個(gè)分析器,為你的詞法分析器新增關(guān)于調(diào)試信息的結(jié)構(gòu)。然后用一個(gè)過(guò)濾器分離調(diào)試信息記號(hào)到另一個(gè)流,這樣,對(duì)兩種類型的.class文件,原來(lái)的分析器都可以正常工作了。

    稍后,我想加一個(gè)"視圖",這是真正的另一個(gè)查看過(guò)濾器的方法。想象一下從一個(gè)詞法分析器(根視圖)中發(fā)射出一個(gè)未加工的記號(hào)流。對(duì)它,我可以非常輕松的構(gòu)建一棵視圖樹(shù)。例如,給出一個(gè)嵌有SQL的Java程序,你可能想為分析或翻譯在輸入流上建立視圖,會(huì)是下面的樣子:

    stream.perspectives.gif (2679 bytes)

    你可以把SQL流或者去掉注釋的Java流交給你的語(yǔ)法分析器處理,分析器中的動(dòng)作也可以去訪問(wèn)注釋流。

    將來(lái),我還會(huì)增加分析器的另一個(gè)功能,去生成記號(hào)流(或文本)作為輸出,就像建立了樹(shù)一樣。用這樣方式,多傳遞分析變得十分自然和簡(jiǎn)單,因?yàn)檎Z(yǔ)法分析器也變成了流的生產(chǎn)者。一個(gè)語(yǔ)法分析器的輸出可以是另一個(gè)語(yǔ)法分析器的輸入。

    Version: $Id: //depot/code/org.antlr/release/antlr-2.7.6/doc/streams.html#1 $

    posted on 2007-11-13 21:25 CowNew開(kāi)源團(tuán)隊(duì) 閱讀(2017) 評(píng)論(5)  編輯  收藏

    評(píng)論

    # re: Antlr中文文檔初稿5(《ANTLR記號(hào)流》) [未登錄](méi) 2007-11-15 09:07 海闊天空
    您的圖全是XX,引用的是您的本地路徑吧?  回復(fù)  更多評(píng)論
      

    # re: Antlr中文文檔初稿5(《ANTLR記號(hào)流》) 2007-11-15 09:09 CowNew開(kāi)源團(tuán)隊(duì)
    @海闊天空
    恩是的,等正式版發(fā)布的時(shí)候再加上。您如果需要那些圖的話可以對(duì)照英文原版文檔。:)  回復(fù)  更多評(píng)論
      

    # re: Antlr中文文檔初稿5(《ANTLR記號(hào)流》) 2007-11-16 21:18 BournCayon
    這些天在看antlr,發(fā)現(xiàn)有這么標(biāo)號(hào)“=>”始終沒(méi)有找到合適理解,請(qǐng)各位幫幫忙,如果理解“=>”這個(gè)標(biāo)號(hào)的含義?

    期待大家.....

    謝謝?。。?!  回復(fù)  更多評(píng)論
      

    # re: Antlr中文文檔初稿5(《ANTLR記號(hào)流》) 2007-11-16 21:20 CowNew開(kāi)源團(tuán)隊(duì)
    =>是做前向測(cè)試的。可以把它想像成C或者JAVA中的“?:”  回復(fù)  更多評(píng)論
      

    # re: Antlr中文文檔初稿5(《ANTLR記號(hào)流》) 2007-11-19 11:49 BournCayon
    的確是這樣,謝謝!  回復(fù)  更多評(píng)論
      


    只有注冊(cè)用戶登錄后才能發(fā)表評(píng)論。


    網(wǎng)站導(dǎo)航:
     
    主站蜘蛛池模板: 免费又黄又爽又猛的毛片| 成年女人免费视频播放体验区| 亚洲国产aⅴ综合网| 亚洲av无码av在线播放| 暖暖在线日本免费中文| 亚洲色成人网站WWW永久四虎 | 456亚洲人成影院在线观| 无码国产精品一区二区免费式直播| 亚洲精彩视频在线观看| 国产精品69白浆在线观看免费| 国产成人亚洲合集青青草原精品| 一区二区无码免费视频网站| 亚洲精品色播一区二区| 色播在线永久免费视频| 日韩久久无码免费毛片软件| 久久久久久A亚洲欧洲AV冫| 免费毛片a线观看| 亚洲欧洲日产韩国在线| 女人18毛片水真多免费看| 免费在线人人电影网| 亚洲日韩中文字幕在线播放| 久9久9精品免费观看| 亚洲 欧洲 视频 伦小说| 日本免费人成黄页网观看视频| 免费无码专区毛片高潮喷水| 亚洲宅男天堂在线观看无病毒| 免费国产黄网站在线观看可以下载| 亚洲成av人片不卡无码| 狠狠久久永久免费观看| 91精品全国免费观看青青| 亚洲国产日韩在线人成下载 | 少妇亚洲免费精品| 日韩免费观看一区| 亚洲欧美日韩国产成人| 亚洲男人第一无码aⅴ网站| 最近中文字幕大全免费版在线| 亚洲国产精品综合久久2007| 免费又黄又硬又爽大片| 小草在线看片免费人成视久网| 亚洲欧美日韩中文无线码| 亚洲精品国偷自产在线|