Antlr的语法学习
一个例子
grammar Hello;
r: 'hello' ID ; // 匹配关键字 hello,然后再匹配一个标识
ID: [a-z]+ ; // 标识由小写字母组成
WS: [ \t\r\n]+ -> skip ; // 忽略空白、制表符,换行
这个语法的解析器,可以接受像 "hello ant"
或者 "hello bee"
这样的句子,但 "hi ant"
或者"hello Ant"
就不行。因为 “hi” 无法和 “hello” 匹配,“Ant” 中有大写字母和 ID 无法匹配。
当解析器接受了 "hello ant"
后,将会得到这样的ast结构:
r
hello
ant
语法结构
从上面可以看出,语法结构最基本的部分是一个grammar ...
加上几条... : ... ;
。
grammar ...
称为语法声明,说明语法的名字。... : ... ;
称为规则,说明该语法的解析器要怎么做。
一个包含所有结构的语法文件是这个样子:
gammar ... ;
options {...}
import ... ;
tokens {...}
channels {...} // 仅用于词法解析器
@...{...}
...
... : ... ;
...
options 说明选项,后面细说。先说import。
引入语法
当一个语法A是是对另一个语法B的少量改动时,可以用引入语法,比如语法A定义了整数的加法运算:
grammar A;
expr : item ( '+' item) *;
item: INT ;
INT: [0-9]+ ;
WS: [ \r\t\n]+ -> skip ;
如果语法B只是需要让item可以是表示变量的标识符,那么可以引入语法A,并重定义item就可以了:
grammar B;
import A;
item : ID ;
ID : [a-z]+ ;
在引入多个语法的时候,如果存在相同名称的规则,以最先引入的为准。如果存在嵌套,嵌套最深的最先引入。
Tokens部分
tokens
用于定义标识符的类型:
tokens { Token_1, ... , Token_n }
多数时候用于定义从外部程序传递来的标识符。
语法层次的动作
语法层次的动作相对于规则层次的动作。用于在规则之外定义一些动作。目前有两个:@header, @members
。
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 ;
这个语法在接受了"10, 9, 8, 7"这个输入后,会输出4 ints
。