天天看點

JAVCC文法檔案 官方文檔翻譯1  概述2  javacc_input3  javacc_options4  option_binding5  production6  lexical_state_list7  regexpr_kind8  regexpr_spec9  expansion_choices10   local_lookahead11   regular_expression12   character_list

題注:

         做Java、C/C++程式設計多年、技術管理多年,天天忙碌于代碼、鎖事之間。終于下定決心咬牙騰點兒時間來寫些東西。萬事開頭難,就先來簡單容易的吧。随着程式設計人員水準的不斷提高,JavaCC也逐漸走入了大家的視線,也開始活躍起來。但其難度确實不小,相應的資料也較少。便鬥膽翻譯了下sun官網上的說明,雖然頁數不多,但卻耗去了将近半年的時間。水準很有限,請閱讀到本資料的同學、老師們多指正,我會及時進行修訂。希望通過學習交流,大家都有所提高。

------ 甯靜以緻遠

正文(如需下載下傳PDF,請到http://download.csdn.net/detail/crownchen/8116515):

JavaCC文法檔案

1  概述

本文檔涵蓋了Javacc文法檔案的複雜文法,并對每個結構給出了詳細的解釋。

文法檔案中的Token,與Java程式設計語言遵循相同的慣例。是以在本文法中使用的辨別符、字元串、字元等等與Java辨別符、Java字元串、Java字元等相同。

本文法中的“空格”也遵循與Java程式設計語言中相同的慣例。也包括注釋的文法。本文法檔案中的大部分注釋都會生成到所産生的解析器/詞法分析器中。

本文法檔案會預先處理進行Unicode轉義,就像Java檔案一樣。(例如:檔案中出現的像\uxxxx一樣的字元串---xxxx為16進制值,在詞法分析前就會轉為相應的Unicode字元)。

以上規則的例外情況:Java操作符“<<”、“>>”、“>>>”、“<<=”、“>>=”、和“>>>=”為JavaCC輸入token清單保留,目的是為了允許友善的嵌套使用token規定。最後,下面列出了Javacc文法檔案中的其他保留字:

EOF IGNORE_CASE JAVACODE LOOKAHEAD
MORE PARSER_BEGIN PARSER_END SKIP
SPECIAL_TOKEN TOKEN TOKEN_MGR_DECLS

在文法規則中使用的任何Java條目,都用斜體表示,并冠以java_字首(如:java_compilation_unit)。

2  javacc_input

javacc_input ::= javacc_options
"PARSER_BEGIN" "(" <IDENTIFIER> ")"
java_compilation_unit
"PARSER_END" "(" <IDENTIFIER> ")"
( production )*
<EOF>

本文法以選項清單開始(為可選項)。然後是以“PARSER_BEGIN(name)”和“PARSER_END(name)”包圍的Java 編譯單元。這些後面,便是文法産生式清單。這些選項(Options )和産生式( productions)後面進行較長的描述。

緊随“PARSER_BEGIN”和“PARSER_END”的name必須相同,并且該辨別符确定了所生成的解析器的名稱。例如:如果name是MyParser,那麼将産生如下的檔案:

MyParser.java: 所生成的Parser。

MyParserTokenManager.java: 所生成的 token管理器 (或掃描器/詞法分析器).。

MyParserConstants.java: 一系列有用的常量。

也會産生一些其他檔案,如:Token.java、ParseException.java等。這些檔案包含了樣闆檔案代碼,對任何文法都一樣,并可以跨文法重複使用(提供文法使用相容選項)。

在PARSER_BEGIN和PARSER_END結構之間的是正常的Java編譯單元(Java術語,一個編譯單元是一個Java檔案的完整内容)。它可以是任意Java編譯單元,隻要它包含一個類聲明名字與所産生的解析器的名字相同(在上面的例子中為“MyParser”)。是以,通常文法檔案中該部分像是下面的樣子:

PARSER_BEGIN(parser_name)

    . . .

classparser_name . .. {

      . . .

    }

    . . .

PARSER_END(parser_name)

JavaCC不對編譯單元進行詳細的檢查,是以文法檔案可能通過JavaCC的檢查,但他們産生的Java檔案在編譯時卻産生錯誤。

如果編譯單元包含了package聲明,它将包含在所有生成的檔案中。如果編譯單元包含了import聲明,它也将包含在所産生的解析器和token管理器檔案中。

所産生的解析器檔案包含編譯單元中的所有内容,以及放在解析器類末尾的所生成的解析器源代碼。對于上面的例子,所産生的解析器看起來像下面的樣子:

    . . .

classparser_name . .. {

      . . .

      // 所産生的解析器代碼插入到此處

    }

    . . .

所生成的解析器包含與文法檔案中每個非終結符(參見 javacode_production和bnf_production)相對應的public方法。與非終結符相關的解析通過調用那些非終結符相對應的方法來完成。不像yacc,在JavaCC裡沒有單一起始符—能夠解析文法中任何非終結符相關的内容。

所生成的Token管理器提供一個public方法:

    Token getNextToken() throws ParseError;

該關于該方法如何使用的更多描述請閱讀:the description of the Java CompilerCompiler API.

3  javacc_options

javacc_options ::= [ "options" "{" ( option_binding )* "}" ]

如果存在選項,則以保留字“options”開頭,後接一個或者多個選項清單,這些選項綁定用大括号{}來包圍。每個選項綁定指定了一個選項的設定。相同的選項不能被多次設定。

選項可以在文法檔案中指定,也可以從指令行(the command line)指定。從指令行指定的選項優先。

選項名稱大小寫不敏感。

4  option_binding

option_binding ::= "LOOKAHEAD" "=" java_integer_literal ";"
| "CHOICE_AMBIGUITY_CHECK" "=" java_integer_literal ";"
| "OTHER_AMBIGUITY_CHECK" "=" java_integer_literal ";"
| "STATIC" "=" java_boolean_literal ";"
| "SUPPORT_CLASS_VISIBILITY_PUBLIC" "=" java_boolean_literal ";"
| "DEBUG_PARSER" "=" java_boolean_literal ";"
| "DEBUG_LOOKAHEAD" "=" java_boolean_literal ";"
| "DEBUG_TOKEN_MANAGER" "=" java_boolean_literal ";"
| "ERROR_REPORTING" "=" java_boolean_literal ";"
| "JAVA_UNICODE_ESCAPE" "=" java_boolean_literal ";"
| "UNICODE_INPUT" "=" java_boolean_literal ";"
| "IGNORE_CASE" "=" java_boolean_literal ";"
| "USER_TOKEN_MANAGER" "=" java_boolean_literal ";"
| "USER_CHAR_STREAM" "=" java_boolean_literal ";"
| "BUILD_PARSER" "=" java_boolean_literal ";"
| "BUILD_TOKEN_MANAGER" "=" java_boolean_literal ";"
| "TOKEN_EXTENDS" "=" java_string_literal ";"
| "TOKEN_FACTORY" "=" java_string_literal ";"
| "TOKEN_MANAGER_USES_PARSER" "=" java_boolean_literal ";"
| "SANITY_CHECK" "=" java_boolean_literal ";"
| "FORCE_LA_CHECK" "=" java_boolean_literal ";"
| "COMMON_TOKEN_ACTION" "=" java_boolean_literal ";"
| "CACHE_TOKENS" "=" java_boolean_literal ";"
| "OUTPUT_DIRECTORY" "=" java_string_literal ";"

1)       LOOKAHEAD:在解析過程中,确定選擇點之前向前看的token數目。預設值為1。該值越小,解析速度越快。該值可能被文法中後面的特定産生式覆寫。請參見lookahead(向前看)如何工作的完整詳細資料中lookahead算法( the lookahead algorithm)的描述。

2)       CHOICE_AMBIGUITY_CHECK:這是一個整數選項,預設值為2。這是在檢查形如“A|B|…”出現多種解釋時進行選擇所需考慮的token數目。例如:如果A和B有相同的兩個token為字首,但沒有第三個,(假設該選項設定為3)那麼JavaCC告訴你需向前看3來消除混淆。并且,如果A和B有相同的三個token為字首,那麼JavaCC隻告訴你需要向前看3或者更多。增加此值會給你更多的關于模糊的資訊(以能夠進行選擇),但是以更多的處理時間為代價。對于像Java文法這樣的大型文法,增加此值将引起更多的檢查時間。

3)       OTHER_AMBIGUITY_CHECK:整數選項,預設值為1。該值是檢查所有其他選擇(如:形如"(A)*","(A)+", 和"(A)?")來解除不明确性所需要考慮的token數目。這将比選擇檢查需要更多時間,是以此值預設值設定為1而不是2。

4)       STATIC:這是一個布爾值選項,預設值為true。如果為true,在所生成的解析器和token管理器的所有方法和類變量被加上static辨別。這隻允許一個解析器對象存在,但它可以改善解析器性能。如果解析器是static的,為了在您的程式一次執行期間進行多次解析,您必須調用ReInit() 方法來重新初始化您的解析器。如果解析器是非靜态的,您可以使用new操作來生成多個解析器,這些解析器可以在不同的線程中并發使用。

5)       DEBUG_PARSER:這是一個布爾選項,預設值為false。該選項用于從生成的解析器中擷取debug資訊。設定該項為true将引發解析器産生動作的trace資訊。Trace可以通過調用所生成的解析器類中的disable_tracing()方法來禁用,也可以随後調用enable_tracing()方法來打開。

6)       DEBUG_LOOKAHEAD:這是一個布爾選項,預設值為false。當DEBUG_PARSER設定為true時,設定該項為true将引發解析器産生其全部行為的trace資訊,并且也引發解析器在向前看操作中産生的執行動作的trace。

7)       DEBUG_TOKEN_MANAGER:這是一個布爾選項,預設值為false。該選項用于從生成的token管理器中擷取debug資訊。設定該項為true将引發token管理器産生其行為trace資訊。該Trace巨大,僅當您遇到詞法錯誤被報告并且不了解原因時使用。典型的,在此情況下,您可以通過檢視trace的最後幾行就可以确定問題。

8)       ERROR_REPORTING:這是一個布爾選項,預設值為true。設定該項為false将引發解析錯誤發生時報告更少的詳細資訊。設定該項為false的唯一原因就是為了改善性能。

9)       JAVA_UNICODE_ESCAPE:這是一個布爾選項,預設值為false。當設定該項為true時,所生成的解析器在發送字元到token管理器之前使用輸入流對象處理Java Unicode轉義符(\u…)。預設情況下,JavaUnicode轉義符不被處理。如果選項USER_TOKEN_MANAGER和USER_CHAR_STREAM任意一個設定為true時,該選項就被忽略。

10)   UNICODE_INPUT:這是一個布爾選項,預設值為false。當設定該項為true時,所生成的解析器使用輸入流對象讀取Unicode檔案(譯注:假設檔案是Unicode檔案)。預設情況下,假設是ASCII檔案。

11)   IGNORE_CASE:這是一個布爾選項,預設值為false。設定該項為true将引發所生成的token管理器在處理token規定和輸入檔案時忽略大小寫。這對編寫像HTML這樣的語言的文法很有用。也可以通過使用下面描述的可替換機制( an alternate mechanism described later)将IGNORE_CASE的作用局部化。

12)   USER_TOKEN_MANAGER:這是一個布爾選項,預設值為false。預設行為是生成一個token管理器,它在所規範的文法的token上工作。如果該選項設定為true,那麼解析器被生成來接受那些來自“TokenManager”類型(該interface被生成到所産生的解析器的目錄)的任何token管理器的Token。(譯注:支援自定義token管理器)

13)   SUPPORT_CLASS_VISIBILITY_PUBLIC:這是一個布爾選項,預設值為true。預設行為是生成帶有Public可見性的支撐類(如:Token.java、ParseException.java等)。如果該選項設定為false,那麼生成帶有包私有(package-private)可見性的類。

14)   USER_CHAR_STREAM:這是一個布爾選項,預設值為false。預設行為是生成一個符合選項JAVA_UNICODE_ESCAPE和UNICODE_INPUT規定的字元流式reader。所生成的token管理器從該流式reader中接受字元。如果該選項設定為true,那麼所生成的token管理器從任何“CharStream.java”類型的流式reader中讀取字元。“CharStream.java”被生成到所生成的解析器的目錄。當USER_TOKEN_MANAGER設定為true時,該選項被忽略。

15)   BUILD_PARSER:這是一個布爾選項,預設值為true。預設行為是生成解析器檔案(上面的例子中的“MyParser.java”)。如果該選項設定為false,那麼将不會生成解析器檔案。典型用法是,當您希望僅生成token管理器并且在使用它時不關聯解析器時,将該選項設定為flase。

16)   BUILD_TOKEN_MANAGER:這是一個布爾選項,預設值為true。預設行為是生成token管理器檔案(上面的例子中的“MyParserTokenManager.java”)。如果該選項設定為false,那麼将不會生成token管理器檔案。設定該項為false的唯一原因,就是當您修訂文法檔案中解析器部分的問題并且不改變詞法規範時,為了節省解析器生成時間而設定。

17)   TOKEN_MANAGER_USES_PARSER:這是一個布爾選項,預設值為false。當設定成true時,所生成的token管理器将包含一個名稱為parse的屬性,用于引用被執行個體化的解析器執行個體(上面例子中的MyParser類型的執行個體)。在token管理器中擁有parser的主要原因是使用詞法動作中的一些邏輯。如果STATIC選項設定為true,此選項無效。

18)     TOKEN_EXTENDS:該選項是一個字元串選項,預設值是“”,意思是所生成的Token類将繼承自java.lang.Object。該選項可以設定為所生成Token類的基類的名稱。

19)     TOKEN_FACTORY:該選項是一個字元串選項,預設值是“”,意思是token将通過調用Token.newToken()方法建立。設定該項時,需設定為一包含public static Token newToken(intofKind, String image)方法的Token factory類的名稱。

20)   SANITY_CHECK:這是一個布爾選項,預設值為true。JavaCC在解析生成過程中,将對文法檔案執行許多文法及語義上的檢查。為了更快的解析生成,一些如左遞歸探測、模棱兩可的探測和空擴充式錯誤用法的檢查,可以通過設定該項為false而被壓縮。注意,這些錯誤(該項設定為false時這些錯誤甚至不會被檢測和報告)的出現可能引起所生成解析器的不符合期望的行為。

21)   FORCE_LA_CHECK:這是一個布爾選項,預設值為false。該項設定控制JavaCC是否執行向前看模棱兩可檢查。預設情況下(該選項是false),在預設的向前看值為1時的所有選擇點,JavaCC執行向前看模棱兩可檢查。而當有明确的向前看規定或者LOOKAHEAD值超過1時,面對這樣的選擇點時JavaCC将不執行向前看模棱兩可檢查。設定該項為true,JavaCC将在所有檢查點執行向前看模棱兩可檢查,而不管文法檔案中的向前看規定。

22)   COMMON_TOKEN_ACTION:這是一個布爾選項,預設值為false。當該項設為true時,對token管理器方法“getNextToken”(see the description of the Java CompilerCompiler API)的每一次調用,都将引發在token管理器掃描完token後,對使用者自定義方法“CommonTokenAction”的調用。使用者必須在TOKEN_MGR_DECLS節中定義該方法。該方法的聲明如下:

voidCommonTokenAction(Token t)      

23)   CACHE_TOKENS:這是一個布爾選項,預設值為false。當該項設為true時,将引發所生成的解析器預先向前看更多的token。這有助于性能的改善。然而,此時(該項設定為true時)互動應用可能不能工作,因為解析器需要與來自輸入流的有效token同步工作(譯注:由于需要與應用互動,是以使用者沒有輸入時,無法預先讀入)。此種情形下,最好保留該選項使用其預設值。

24)     OUTPUT_DIRECTORY:該選項是一個字元串選項,預設值是目前目錄。該項控制所生成的輸出檔案存放位置。

5  production

production ::= javacode_production
| regular_expr_production
| bnf_production
| token_manager_decls

JavaCC中有四種産生式。 javacode_production和bnf_production用于定義來自所生成parser的文法。regular_expr_production用于定義token的文法—token管理器由該資訊生成(就像來自parser文法中的内部token規範一樣)。 token_manager_decls用于開頭的聲明插入到所生成token管理器中。

5.1    javacode_production

javacode_production ::= "JAVACODE"
java_access_modifier java_return_type java_identifier "(" java_parameter_list ")"
java_block

JAVACODE産生式是用Java Code寫一些可以代替通常EBNF表達式的産生式的方法。當需要辨識非上下文自由或者由于某種原因難于寫文法的時候,這會很有用。JAVACODE用法的例子見下面。此例中,非終結符“skip_to_matching_brace”消費輸入流中的token,一直到比對到右大括号“}”(右括号假設已經被掃描過):

JAVACODE      
voidskip_to_matching_brace() {      
Tokentok;      
int nesting = 1;      
while (true) {      
tok = getToken(1);      
if (tok.kind == LBRACE) nesting++;      
if (tok.kind == RBRACE) {      
nesting--;      
if (nesting == 0) break;      
        }      
tok = getNextToken();      
      }      
    }      

須小心使用JAVACODE産生式。你可以通過這些産生式獲得更多想要的,但JavaCC卻隻簡單的把它當做黑盒(盡可能執行它的分析任務)。當JAVACODE産生式出現在選擇點(choice points)時,便會帶來問題。例如,如果上述JAVACODE産生式被從下面的産生式引用時:

void NT() :      
  {}      
  {      
skip_to_matching_brace()      
  |      
some_other_production()      
  }      

那麼JavaCC不知道在這兩個選擇中如何抉擇。另一方面,如果JAVACODE産生式被用在非選擇點時,則沒有問題,例如下面的例子:

void NT() :      
  {}      
  {      
    "{" skip_to_matching_brace()      
  |      
    "(" parameter_list() ")"      
  }      

在選擇點的JAVACODE産生式,可以以詞法、文法的LOOKAHEAD為先導,例如:

void NT() :      
  {}      
  {      
LOOKAHEAD( {errorOccurred} ) skip_to_matching_brace()      
  |      
    "(" parameter_list() ")"      
  }      

JAVACODE産生式的預設通路控制修飾符是包私有的(package private)。

5.2    bnf_production

bnf_production ::= java_access_modifier java_return_type java_identifier "(" java_parameter_list ")" ":"
java_block
"{" expansion_choices "}"

BNF産生式是用于描述JavaCC文法的标準生産式。每個BNF産生式的左邊是非終結符描述。然後産生式用右邊的BNF展開式來定義此非終結符。非終結符被确切地寫成Java方法聲明的樣子。因為每個非終結符在生成的parser中都被轉換成一個方法,是以這種書寫方式使得這種關聯更加明晰。非終結符的名稱就是方法的名稱,聲明的參數和傳回值意味着在解析樹上向上或者向下傳遞值。正如後面所講,産生式右邊的非終結符被寫成方法調用,是以值在解析樹上向上或者向下的傳遞與方法調用與傳回具有相同的樣式。BNF産生式的預設通路控制修飾符是public。

BNF産生的右邊有兩部分。第一部分是任意的Java聲明和代碼的集合(Java塊),該代碼生成時,位于為Java非終結符生成的方法裡的前面位置(譯注:存在于方法中,在方法的開始位置)。是以,每次該非終結符在解析過程中被使用時,這些聲明和代碼都會被執行。該部分的聲明對于在BNF展開式中動作的所有Java代碼都是可見的。JavaCC不對這些聲明和代碼做任何處理,除了跳過配置的結束大括号、收集遇到的所有文本。是以,Java編譯器能夠檢測JavaCC處理過的該段代碼的錯誤。

右邊第二部分是BNF展開式。後面會有描述。

5.3    regular_expr_production

regular_expr_production ::= [ lexical_state_list ]
regexpr_kind [ "[" "IGNORE_CASE" "]" ] ":"
"{" regexpr_spec ( "|" regexpr_spec )* "}"

正規表達式用來定義詞法條目,這些詞法條目由生成的token管理器來處理。Token管理器如何工作的較長的描述參見 this minitutorial (click here)。本文描述了規範的詞法條目的句法方面,而the minitutorial 描述了這些句法結構如何與token管理器的工作機理連接配接起來。

一個正規表達式産生式起始于一個詞法狀态的清單。有一個标準的詞法狀态叫“DEFAULT”。如果詞法狀态清單( lexical state list)被省略,正規表達式生産式應用詞法狀态“DEFAULT”。

緊随其後是正規表達式産生式種類的描述。(參見下面來了解這種此意思是什麼,see below for what this means)

這之後是一個選項“[IGNORE_CASE]”。如果該項存在,正規表達式産生式是大小寫不敏感的—它與IGNORE_CASE選項功效相同,除了它的作用範圍是本地的,隻适于該正規表達式産生式。

然後是正規表達式規格清單,這些規格描述了該正規表達式産生式之詞法條目的更多資訊。

5.4    token_manager_decls

token_manager_decls ::= "TOKEN_MGR_DECLS" ":" java_block

Token管理器的聲明以保留字"TOKEN_MGR_DECLS"開始,其後是“:”,然後是一系列Java聲明和Java語句(Java塊)。這些聲明和塊被寫入到所生成的token管理器中,并可從詞法動作( lexical actions)中通路。參見token管理器微教程( the minitutorial on the token manager)擷取更詳細資料。

在JavaCC文法檔案中僅有一個token管理器聲明。

6  lexical_state_list

lexical_state_list ::= "<" "*" ">"
| "<" java_identifier ( "," java_identifier )* ">"

詞法狀态清單描述了一系列與正規表達式産生式(regular expression production)相适應的詞法狀态。如果被寫成“<*>”,那麼正規表達式産生式适用于所有詞法狀态。否則,它适用于尖括号内辨別符清單裡的所有詞法狀态。

7  regexpr_kind

regexpr_kind ::= "TOKEN"
| "SPECIAL_TOKEN"
| "SKIP"
| "MORE"

下面描述正規表達式産生式的種類。共有四種:

1)       TOKEN:本正規表達式産生式中的正規表達式描述了文法中的tokens。該token管理器為該正規表達式每一個比對建立一個token對象,并傳回該對象給parser。

2)       SPECIAL_TOKEN:本正規表達式産生式中的正規表達式描述了特别的tokens。這些特殊的token與普通token一樣,隻是在解析過程中沒有意義---也就是說BNF産生式将忽略它們。

3)       SKIP:與在本正規表達式産生式中正規表達式的比對是被該token管理器簡單的跳過(忽略)。

4)       MORE:有時它對于漸近地建立一個在parser上傳遞的token很有用。與此種正規表達式的比對被存儲到一個緩存中,直到下一個TOKEN或者SPECIAL_TOKEN比對。然後在緩存中的所有比對和最終TOKEN/SPECIAL_TOKEN的比對被連接配接在一起形成一個TOKEN/SPECIAL_TOKEN,在parser上被傳遞。如果一個SKIP正規表達式的比對後跟一系列MORE比對,那麼緩存中的内容被丢棄。

8  regexpr_spec

regexpr_spec ::= regular_expression [ java_block ] [ ":" java_identifier ]

正規表達式規格開始了作為本正規表達式産生式一部分的詞法條目的實際描述。每一個正規表達式産生式可能包含任意數目的正規表達式規格。

每一個正規表達式規格包含一個正規表達式,其後跟一個可選的Java塊。然後是詞法狀态的辨別符(這也是可選的)。每當本正規表達式被比對時,詞法動作(如果有的話)将被執行,并後跟一些共同的token動作(common token actions)。然後采取依賴于正規表達式産生式種類(regular expression production kind)的動作。最後,如果一個詞法狀态被指定的話,token管理器為進一步處理而遷移到那個詞法狀态(token管理器初始時開始于“DEFAULT”狀态)。

9  expansion_choices

9.1    expansion_choices

expansion_choices ::= expansion ( "|" expansion )*

展開選擇被寫成一個或者多個由“|”分隔的展開式清單。展開選擇的合法解析的集合是任何一個包含展開式的合法解析。

9.2    expansion

expansion ::= ( expansion_unit )*

展開式被寫成一系列展開式單元。這些展開式單元合法解析的串聯是一個展開式的合法解析。

例如,展開式“{" decls() "}”由三個展開式單元- “{”、decls()和“}”組成。展開式的比對是與每個獨立的展開式單元相應的比對的串聯(譯注:都要比對)—此例中,将是任何一個以“{”開始而以“}”結束的字元串,并且在它們中間包含對decls()的比對。

9.3    expansion_unit

expansion_unit ::= local_lookahead
| java_block
| "(" expansion_choices ")" [ "+" | "*" | "?" ]
| "[" expansion_choices "]"
| [ java_assignment_lhs "=" ] regular_expression
| [ java_assignment_lhs "=" ] java_identifier "(" java_expression_list ")"

一個展開式單元可以是一個本地向前看的規格(local LOOKAHEAD specification)。這訓示所生成的parser在遇到選擇點時如何做決策。LOOKAHEAD規格工作機理詳細資訊及如何寫LOOKAHEAD規格請參見click here to visit the minitutorial onLOOKAHEAD。

一個展開式單元可以是一系列Java聲明和用大括号括起來的代碼(Java塊)。這些也叫作解析動作(parser actions)。這些将被生成到解析非終結符的方法中,并放到适當的位置。在解析過程中遇到該選擇點時,本塊将被成功執行。當JavaCC處理該Java塊時,它不進行詳細的句法和語義檢查。是以Java編譯器可以發現被JavaCC處理過的動作中的錯誤。這些動作在向前評估( lookahead evaluation)過程中不會執行。

一個擴充式單元可以是用括号括起來的一個或者多個擴充選擇( expansion choices)的集合。此種情況下,該擴充單元的合法解析是嵌套擴充選擇的任何合法解析。括起來的擴充選擇集合可以後跟(可選擇項,也可以不跟):

1)       “+”:該擴充單元的任何合法解析是括号内擴充選擇集合合法解析的一次或者多次重複。

2)       “*”:該擴充單元的任何合法解析是括号内擴充選擇集合合法解析的零次或者多次重複。

3)       “?”:該擴充單元的任何合法解析是嵌套擴充選擇的任何合法解析或者空token系列。可替換的文法是用中括号括起來的擴充選擇“[…]”。

一個擴充單元可以是一個正規表達式(regular expression)。那麼該擴充單元的合法解析是任何比對該正規表達式的token。當一個正規表達式被比對,它會建立一個token(Token)類的對象。該對象可以通過把它賦予一個變量而被通路,該變量由正規表達式字首“variable=”來設定。通常,你可以使用任何有效的Java指派語句“=”左邊的式子(譯注:Java指派語句左手邊必須是一個變量,此處是說凡是Java指派語句中左手邊的那個變量所允許的寫法,此處都可以支援)。該指派在向前看評估( lookahead evaluation)時不會執行。

一個擴充式單元可以是一個非終結符(上述文法的最後一個選擇)。此時,它将使用方法調用的形式,并将非終結符的名字作為該方法的名字。一個該非終結符的成功解析引發被調用方法所傳入參數上的操作及傳回操作值(此種情況下非終結符不能被聲明為“void”類型)。

10   local_lookahead

local_lookahead ::= "LOOKAHEAD" "(" [ java_integer_literal ] [ "," ] [ expansion_choices ] [ "," ] [ "{" java_expression "}" ] ")"

本地向前看規格用于影響所生成的解析器在遇到文法中各種選擇點時進行選擇的方式。本地向前看規格開始于保留字“LOOKAHEAD”,後跟一系列用小括号括起來的向前看限制。共有三種不同類型的向前看限制:向前看限制(整數值),詞法向前看(擴充式選擇),和文法向前看(大括号内的表達式)。至少有一個向前看限制。如果有多個限制,必須用逗号(“,”)分隔。

向前看工作機理的較長的描述請參見click here to visit the minitutorial onLOOKAHEAD。每種類型的向前看限制簡要描述如下:

1)       向前看限制:該項是向前看的token最多個數,用于(面臨選擇點時的)選擇确定。該值将覆寫LOOKAHEAD選項設定的預設值。該向前看限制僅用于本地向前看規格中的本地的選擇點。如果本地向前看規格沒有選擇點,本向前看限制(如果有)将被忽略。

2)       詞法向前看:該項是擴充式(或者是擴充選擇),用于确定是否采用本地向前看規格适用的特殊選擇。如果該項未提供,解析器使用在向前看決定過程中所選擇的擴充式。如果本地向前看規格不在選擇點(choice point),那麼詞法向前看(如果有)将被忽略。

3)       文法向前看:該項是一個布爾表達式,每當在解析過程中遇到該點時該表達式會被評估。如果表達式評估結果為真(true),解析将正常繼續。如果表達式評估為假(false)并且本地向前看規格在選擇點,那麼目前選擇不被采用,而采用下一個選擇。如果表達式評估為假(false)并且本地向前看規格不在選擇點,那麼解析器放棄解析并帶有一個解析錯誤。不像其他兩個向前看限制在沒有選擇點時被忽略,文法向前看限制總是被評估。實際上,甚至如果在一些其他詞法檢查的評估過程中遇到時文法向前看限制也被評估(更詳細的請參見click here to visit the minitutorial onLOOKAHEAD)。

向前看限制的預設值:如果設定了本地向前看規定,但不是所有向前看限制都包括,那麼未設定的限制則被賦為預設值,如下:

1)       如果向前看限制沒被設定,并且文法向前看被設定,那麼向前看限制的預設設定是整數最大值(2147483647)。這本質上實作了“無限向前看”-也就是說,比對被設定的文法向前看時,有必要向前看多少token就向前看多少。

2)       如果向前看限制和文法向前看都未設定(這意味着設定了語義向前看),則向前看限制預設值為0。這意味着文法向前看不被執行(顯而易見執行通過),并且隻有語義向前看被執行。

3)       如果文法向前看沒有設定,它預設為所設定的本地向前看的自然選擇。如果本地向前看規定不在選擇點,那麼文法向前看被忽略—是以預設值無實質作用。

4)       如果語義向前看不設定,它預設為布爾表達式“true”。也就是說,顯而易見它執行通過。

11   regular_expression

11.1       regular_expression

regular_expression ::= java_string_literal
| "<" [ [ "#" ] java_identifier ":" ] complex_regular_expression_choices ">"
| "<" java_identifier ">"
| "<" "EOF" ">"

在文法檔案中有兩個地方需要寫正規表達式:

1)       在正規表達式規定(正規表達式産生式的部分)中,

2)       當擴充單元帶有擴充時。當一個正規表達式用于此種情況時,正規表達式在本地被定義為下面的方式,然後擴充單元中通過它的标簽來引用它:

<DEFAULT>TOKEN :      
    {      
regular expression      
    }      

也就是說,正規表達式的使用方式可以使用另一種方式來重寫。

token管理器比對正規表達式的完整細節請參見token管理器微教程(the minitutorial on the token manager)。文法結構描述如下。

第一種正規表達式是字元串字面值。如果token管理器處于該正規表達式适用的詞法狀态并且輸入流中的下一字元集合與該字元串字面值相同(有時忽略大小寫),那麼被解析的輸入比對該表達式。

一個正規表達式也可以是一個更複雜的正規表達式,該複雜正規表達式使用更多的正規表達式(比僅僅使用字元串字面值複雜)。這樣的正規表達式被放到尖括号“<…>”中,并且可選擇性的使用一個辨別打上标簽。該标簽可用于來自擴充式單元或者另一個正規表達式中對該正規表達式的引用。如果标簽前置“#”,那麼該正規表達式不能被擴充式單元引用,但可以被另一個正規表達式引用。當“#”存在時,該正規表達式被作為一個“私有正規表達式”被引用。

一個正規表達式可以是一個對另外某個打上标簽的正規表達式的引用,此時該标簽保用尖括号“<…>”包圍起來。

最後,一個正規表達式可以是一個對先前定義的正規表達式“<EOF>”(它比對檔案結束)的引用。

私有正規表達式不被token管理器比對為token。它們的目的僅僅是為了其他更複雜正規表達式定義的友善。

考慮下面的例子,Java中浮點資料字面值的定義:

TOKEN :      
{      
<FLOATING_POINT_LITERAL:      
        (["0"-"9"])+ "." (["0"-"9"])* (<EXPONENT>)? (["f","F","d","D"])?      
      | "." (["0"-"9"])+ (<EXPONENT>)? (["f","F","d","D"])?      
      | (["0"-"9"])+<EXPONENT> (["f","F","d","D"])?      
      | (["0"-"9"])+ (<EXPONENT>)? ["f","F","d","D"]      
|      
< #EXPONENT: ["e","E"] (["+","-"])? (["0"-"9"])+ >      
}      

本例中,定義token FLOATING_POINT_LITERAL時使用了另外一個token--- EXPONENT的定義。EXPONENT标簽前的“#”表示它(指EXPONENT标簽)的存在僅僅是為了定義另一個token(此處指的是FLOATING_POINT_LITERAL)。FLOATING_POINT_LITERAL的定義不受是否存在“#”的影響。然而,token管理器的行為卻是這樣:如果“#”被忽略,token管理器将錯誤的認為像E123這樣的字元串是一個EXPONENT種類的合法token(代替Java文法中的IDENTIFIER)。

11.2       complex_regular_expression_choices

complex_regular_expression_choices ::= complex_regular_expression ( "|" complex_regular_expression )*

複雜正規表達式選擇由一個或者多個由“|”分離的複雜正規表達式清單組成。一個複雜正規表達式選擇的比對就是對組成該選擇項的任一複雜正規表達式的比對。

11.3       complex_regular_expression

complex_regular_expression ::= ( complex_regular_expression_unit )*

一個複雜正規表達式是一系列複雜正規表達式單元。對複雜正規表達式的比對就是對這些複雜正規表達式單元一連串的比對。

11.4       complex_regular_expression

complex_regular_expression_unit ::= java_string_literal
| "<" java_identifier ">"
| character_list
| "(" complex_regular_expression_choices ")" [ "+" | "*" | "?" ]

一個複雜正規表達式單元可能是一個字元串字面值,此種情況下對該單元的精确的一個比對就是字元串字面值本身。

一個複雜正規表達式單元可以是另一個正規表達式的引用。被引用的正規表達式必須設定标簽以能夠(通過該标簽)被引用。該單元的比對就是該引用正規表達式的所有比對。正規表達式中此類引用不能在token之間産生依賴循環。

一個複雜的正規表達式單元可以是一個字元清單。字元清單是是一種字元集合的定義方式。此種複雜正規表達式單元的比對是任何該字元清單允許的字元。

一個複雜的正規表達式單元可以是被括号括起來的一系列複雜正規表達式單元選擇。此時,該單元的合法比對是這些嵌套選擇的任一合法比對。被括号括起來的一系列選擇可以後跟以下符号(後跟符号是可選項):

1)       “+”:該單元的任一合法比對是一次或者多次括号内選擇系列的合法比對的重複。

2)      “*”:該單元的任一合法比對是0次或者多次括号内選擇系列的合法比對的重複。

3)      “?”:該單元的任一合法比對是空串或者任一嵌套選擇的合法比對。

注意:不像BNF擴充式,正規表達式“[…]”不等價于正規表達式“(…)?”。這是因為[…]結構被用于描述正規表達式中的字元清單。

12   character_list

12.1       character_list

character_list ::= [ "~" ] "[" [ character_descriptor ( "," character_descriptor )* ] "]"

一個字元清單描述了一系列字元。一個字元清單的合法比對是該集合中的任一字元。一個字元清單是一個方括号括起來的用逗号分隔的字元描述符清單。每一字元描述符描述了一個單一字元或者字元的範圍(參見下面的字元描述符character descriptor ),并且被添加到字元清單的字元集合中。如果字元清單有字首“~”,它表示字元集合是未在指定集合中的任一UNICODE字元。

12.2       character_descriptor

character_descriptor ::= java_string_literal [ "-" java_string_literal ]

一個字元描述符可以是一個單一的字元字元串字面值,此時它描述了一個包含那個字元的單元素集合;或者它是被“-”分隔的兩個單一字元字元串字面值,此時它描述的是在這兩個字元之間範圍的所有字元集合,并包含這兩個字元。

------------------------------------------------  END ------------------------------------------------