查看“Grammars.md”的源代码
←
Grammars.md
跳到导航
跳到搜索
因为以下原因,您没有权限编辑本页:
您请求的操作仅限属于该用户组的用户执行:
用户
您可以查看和复制此页面的源代码。
{{MARKDOWN}} # Grammar Structure 语法结构 Grammar文件本质上是语法声明,后跟Rule列表,具有一般形式: ``` /** Optional javadoc style comment */ grammar Name; ① options {...} import ... ; tokens {...} channels {...} // lexer only @actionName {...} rule1 //parser和lexer rule,可能混合在一起 ... ruleN ``` 包含grammar`X`的文件名必须称为`X.g4`。 您可以按任意顺序指定options、imports、token规范和actions。 options、imports和token规格最多可以有一个。 除header ①外,所有这些元素都是可选的,至少有一条Rule。 Rule采用基本形式: ``` ruleName : alternative1 | ... | alternativeN ; ``` Parser rule名称必须以小写字母开头,而lexer rule必须以大写字母开头。 在`grammar`头上没有前缀的grammar是组合combined grammars,可以包含lexical和parser rule。 要创建只允许parser rules的parser grammar,请使用以下头。 ``` parser grammar Name; ... ``` 而且,对应的,纯lexer grammar看起来像这样: ``` lexer grammar Name; ... ``` 只有lexer语法可以包含`mode`规范。 只有lexer grammars可以包含自定义channels规范 ``` channels { WHITESPACE_CHANNEL, COMMENTS_CHANNEL } ``` 然后可以像lexer rule中的枚举一样使用这些channels: ``` WS : [ \r\t\n]+ -> channel(WHITESPACE_CHANNEL) ; ``` 第15.5节,[Lexer Rules](http://pragprog.com/book/tpantlr2/the-definitive-antlr-4-reference)和第15.3节, [Parser Rules](http://pragprog.com/book/tpantlr2/the-definitive-antlr-4-reference) 包含有关rule语法的详细信息。 15.8节,Options描述了grammar options,15.4节,Actions和Attributes提供了有关grammar-level actions的信息。 ## Grammar Imports 语法导入 语法`imports`允许您将grammar分解为逻辑块和可重用的块,正如我们在[Importing Grammars]中看到的那样(http://pragprog.com/book/tpantlr2/the-definitive-antlr-4-reference). ANTLR对待导入的grammars非常类似于面向对象的编程语言对待父类。 grammar继承了导入grammar中的所有rules,tokens specifications和已命名的actions。 “main grammar”中的Rules覆盖导入grammars中的Rule以实现继承。 可以将`import`看作是一条智能的include语句(导入是会主动不包含已经定义的Rule)。 所有导入的结果都合并成单一的combined grammar; ANTLR代码生成器看到了完整的grammar,并且不知道有导入的grammar。 为了处理主grammar,ANTLR工具将所有导入的grammar加载到subordinate grammar objects中。 然后,它将rules、token types和已命名的actions从导入的grammar合并到主grammar中。 在下图中,右侧的grammar说明了在grammar `MyELang` 中导入grammar 'ELang' 的效果。 <img src=images/combined.png width=400> `MyELang`继承规则`stat`、`WS`和`ID`,但重写rule`expr`并添加`INT`。 下面的示例构建和测试运行表明,`MyELang`可以识别整数表达式,而原始的`ELang`不能。 第三个错误的输入语句会触发一条错误消息,该消息还表明解析器正在寻找 `MyElang` 的expr而不是 `Elang`。 ``` $ antlr4 MyELang.g4 $ javac MyELang*.java $ grun MyELang stat => 34; => a; => ; => EOF <= line 3:0 extraneous input ';' expecting {INT, ID} ``` 如果主语法或任何导入的语法中存在modes,那么导入过程将导入这些modes,并在它们不是覆盖的时候合并它们的Rules。 如果有任何因为其所有Rule都已被该mode外的Rule覆盖,该mode变为空, 则该Mode将被丢弃。 如果有任何 `tokens` specifications,则主要语法将合并token集。 如果有任何`channel` specifications,主语法将合并channel集。 任何已命名的actions(如`@embers`)都将被合并。 通常,您应该避免在导入语法中避免named actions和actions的rules,因为这限制了它们的重用。 ANTLR也会忽略导入语法中的任何options。 导入的语法也可以导入其他语法。 ANTLR以深度优先的方式寻找所有导入语法。 如果两个或多个导入的语法定义了rule `r`,ANTLR将选择它找到的“r”的第一个版本。(译者注:从根开始遇到的第一个) 在下图中,ANTLR按照`Nested`、`G1`、`G3`、`G2`的顺序检查语法。 <img src=images/nested.png width=350> “Nested” 包括来自 `G3` 的 rule `r` ,因为它在`G2` 中的 `r` 之前看到该版本。 并非每种语法都能导入其他语法: * Lexer grammars可以导入lexers,包括包含modes的lexers。 * Parsers可以导入parsers。 *Combined grammar可以导入没有modes的parsers或lexers。 ANTLR在主要lexer grammar中将导入的Rule添加到Rule列表的末尾。 这意味着主grammar中的lexer rules优先于导入rules。 例如,如果主grammar定义了rule `IF : ’if’ ;` 导入的grammar定义了 rule `ID : [a-z]+ ;` (它还识别`if`),导入的`ID`不会隐藏主grammar的`IF`Token定义。 ## Tokens Section (Tokens分段) `tokens`分段的目的是定义没有相关lexical Rule的grammar所需的token types。基本语法是: ``` tokens { Token1, ..., TokenN } ``` 大多数情况下,tokens section用于定义语法中的actions所需的token类型,如第10.3节所示。 [Recognizing Languages whose Keywords Aren’t Fixed](http://pragprog.com/book/tpantlr2/the-definitive-antlr-4-reference): ``` // 显式定义关键字token类型,避免隐式定义警告 tokens { BEGIN, END, IF, THEN, WHILE } @lexer::members { // lexer中用于分配token类型的关键字映射 Map<String,Integer> keywords = new HashMap<String,Integer>() {{ put("begin", KeywordsParser.BEGIN); put("end", KeywordsParser.END); ... }}; } ``` `tokens`section实际上只是定义了一组要添加到整个集合中的Token。 ``` $ cat Tok.g4 grammar Tok; tokens { A, B, C } a : X ; $ antlr4 Tok.g4 warning(125): Tok.g4:3:4: implicit definition of token X in parser $ cat Tok.tokens A=1 B=2 C=3 X=4 ``` ##语法层面的Action 当前,在语法Rule之外仅使用两个定义的named actions (用于Java目标): `header` 和 `members`。 前者在识别器类定义之前将代码注入生成的识别器类文件,后者将代码作为字段和方法注入识别器类定义。 对于combined grammars,ANTLR将操作注入parser和lexer。 要将action限制为生成的parser或lexer,请使用 `@parser::name` 或 `@lexer::name`。 下面是一个语法为生成的代码指定包的示例: ``` grammar Count; @header { package foo; } @members { int count = 0; } list @after {System.out.println(count+" ints");} : INT {count++;} (',' INT {count++;} )* ; INT : [0-9]+ ; WS : [ \r\t\n]+ -> skip ; ``` 语法本身应该在目录`foo`中,以便ANTLR在同一个`foo`目录中生成代码(至少在不使用`-o‘ANTLR工具选项时): ``` $ cd foo $ antlr4 Count.g4 # generates code in the current directory (foo) $ ls Count.g4 CountLexer.java CountParser.java Count.tokens CountLexer.tokens CountBaseListener.java CountListener.java $ javac *.java $ cd .. $ grun foo.Count list => 9, 10, 11 => EOF <= 3 ints ``` Java编译器希望包 `foo` 中的类位于目录 `foo` 中。
返回至“
Grammars.md
”。
导航菜单
个人工具
登录
命名空间
页面
讨论
变体
已展开
已折叠
查看
阅读
查看源代码
查看历史
更多
已展开
已折叠
搜索
导航
首页
最近更改
随机页面
MediaWiki帮助
工具
链入页面
相关更改
特殊页面
页面信息