JAVCC语法文件 官方文档翻译

题注:

         做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" "(" ")"

java_compilation_unit

"PARSER_END" "(" ")"

( production )*

本语法以选项列表开始(为可选项)。然后是以“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)       当扩展单元带有扩展时。当一个正则表达式用于此种情况时,正则表达式在本地被定义为下面的方式,然后扩展单元中通过它的标签来引用它:

TOKEN :
    {
regular expression
    }

也就是说,正则表达式的使用方式可以使用另一种方式来重写。

token管理器匹配正则表达式的完整细节请参见token管理器微教程(the minitutorial on the token manager)。语法结构描述如下。

第一种正则表达式是字符串字面值。如果token管理器处于该正则表达式适用的词法状态并且输入流中的下一字符集合与该字符串字面值相同(有时忽略大小写),那么被解析的输入匹配该表达式。

一个正则表达式也可以是一个更复杂的正则表达式,该复杂正则表达式使用更多的正则表达式(比仅仅使用字符串字面值复杂)。这样的正则表达式被放到尖括号“<…>”中,并且可选择性的使用一个标识打上标签。该标签可用于来自扩展式单元或者另一个正则表达式中对该正则表达式的引用。如果标签前置“#”,那么该正则表达式不能被扩展式单元引用,但可以被另一个正则表达式引用。当“#”存在时,该正则表达式被作为一个“私有正则表达式”被引用。

一个正则表达式可以是一个对另外某个打上标签的正则表达式的引用,此时该标签保用尖括号“<…>”包围起来。

最后,一个正则表达式可以是一个对先前定义的正则表达式“”(它匹配文件结束)的引用。

私有正则表达式不被token管理器匹配为token。它们的目的仅仅是为了其他更复杂正则表达式定义的方便。

考虑下面的例子,Java中浮点数据字面值的定义:

TOKEN :
{
 
  
        (["0"-"9"])+ "." (["0"-"9"])* ()? (["f","F","d","D"])?
      | "." (["0"-"9"])+ ()? (["f","F","d","D"])?
      | (["0"-"9"])+ (["f","F","d","D"])?
      | (["0"-"9"])+ ()? ["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 ------------------------------------------------


你可能感兴趣的:(JavaCC)