3. Antlr 语法纲要

1. 使用ANTLR语法识别常见语言模式

对于一门计算机语言的语法,有4种基本的抽象语言模式:

  • 序列:即一列元素
  • 选择:在多种可选方案中作出选择
  • 词法符号依赖:一个词法符号需要和某处的另外一个词法符号配对,例如左右括号匹配。
  • 嵌套结构:一种自相似的语言结构

接下来就介绍一下Antlr如何表达这些模式

1.1 序列模式
  • 使用类似’RETR’ / ‘\n’(单引号包围)等的常量字符串来表示任意简单字符序列,使用 INT(大写字符串) 这种可表示某种类型的词法符号
    例如 retr : 'RETR' INT '\n' 可匹配 “RETR 整数 换行符” 这样的序列。
    例子: RETR 1
  • 使用 + * ?表示元素可出现次数,使用小写字符串表示不同的规则
    + 处理一个或多个相同元素
    * 处理零个或多个相同元素
    ? 处理可选元素,即0个或1个元素
    例如:
file : (row '\n')*;				// 以一个'\n'作为终止符的序列
row  : field (',' field)* ;		// 以一个','作为分隔符的序列
field: INT;						// 假设字段都是整数
1.2 选择模式(多个备选分支)

使用 | 符号作为或者来表达编程语言中的选择模式。
例1 : field : INT | STRING
例2 :

stmt : node_stmt
     | edge_stmt
     | attr_stmt
     | id '=' id
     ;
1.3 词法符号依赖模式

使用一个序列来指明所有配对的符号,通常这些符号会把其他元素分组或者包裹起来。
例1: vector : '[' INT+ ']' ;
例2: expr : expr '(' exprList? ')';
例3: object : '{' pair (',' pair)* '}';

1.4 嵌套模式

嵌套的词组是一种自相似的语言结构,即子词组遵循相同的结构。
例1:

stat: 'while' '(' expr ')' stat
    | '{' stat* '}'
    ...
    ;

以上语句中 stat 是一个循环结构,可以是一个语句或者由花括号包裹的一组语句。

2. 优先级、左递归、结合性的处理
  • 优先级的处理
    对于某些输入文本而言,有些规则存在歧义。例如如下规则:
expr : expr '*' expr
     | expr '+' expr
     | INT
     ;

对于 1+23 这样的输入文本而言,有 1 + (23) 和 (1+2) * 3 两种理解。
ANTLR通过优先选择位置靠前的备选分支来解决歧义问题,隐式地允许我们指定运算符优先级。

  • 结合性的处理
    默认情况下,ANTLR将运算符从左向右进行结合。但是有一些运算符,例如指数运算符是从右向左结合的,例如 2^3^4正确解释应该是 2^(3^4)。ANTLR允许我们手工指定结合性,如下:
expr : expr '^'<assoc=right> expr
     | INT
     ;
  • 左递归
    左递归规则指的是在某个备选分支的最左侧以直接或者间接方式调用了自身。ANTLR4 已经能够处理直接左递归,但是无法处理间接左递归。间接左递归如下,对于ANTLR来说不可行:
expr : expo
	 | ...
	 ;
expo : expr '^'<assoc=right> expr ;
3. 识别常见的词法结构

ANTLR允许词法规则和语法规则在同一个语法文件中,但是这是语言识别过程中的两个不同阶段,我们必须告诉ANTLR每条规则对应的阶段。方式: 词法规则以大写字母开头,语法规则以小写字母开头。

常见词法符号的词法规则:

  • 匹配标识符
    一个基本标识符就是一个由大小写字母组成的字符序列,规则如下:
    ID : ('A'..'Z'|'a'..'z')+;
    ANTLR也支持正则表达式中的类似规则,如下:
    ID : [A-Za-z]+;
    类似ID的规则有时候会和其他词法规则或者字符串常量值产生冲突,比如’enum’。
    ANTLR解决这种冲突的方法是:
    首先ANTLR会从词法规则中筛选出所有的字符串常量,并将它们和词法规则放在一起。所以 ‘enum’ 这样的字符串常量会被隐式地定义为词法规则,然后放置在语法规则之后,显示定义的词法规则之前。
    而ANTLR词法分析器解决歧义问题的方法是优先使用位置靠前的词法规则,所以像’enum’这样的词比显示定义的词法具有更高优先级。
    另外,ANTLR会自动将词法规则放置在语法规则之后。
enumDef : 'enum' '{' ... '}';
...
ID : [a-zA-Z]+;
  • 匹配数字
    整数比较容易匹配,但是浮点数稍微复杂一点。
    浮点数的格式可以是 xxx.xxx 也可以是 .xxx,规则可以如下:
FLOAT : DIGIT+'.'DIGIT*
	  |       '.'DIGIT+
	  ;
fragment
DIGIT : [0-9] ;

将一条规则声明为fragment是说明改规则本身不是一个词法符号,只会被其他的词法规则使用,即不能在文法规则中引用。

  • 匹配字符串常量
    .*? 是非贪婪模式(匹配数量最少的字符,即获取一些字符直到发现匹配后续子规则的字符为止),.*是贪婪模式,会消费掉一切匹配的字符,所以会把 " 消费掉。
    ESC片段是为了让字符串中允许出现转义双引号的语法和转义字符的语法。由于ANTLR语法本身需要对转义字符\进行转义,因此需要\来表示单个反斜杠字符。
STRING: '"' (ESC|.)*? '"';
fragment
ESC : '\\"' | '\\\\' ;
  • 匹配注释和空白字符
    当词法分析器匹配到注释和空白字符的时候,我们并不需要进行语法分析,所以通常需要将它们丢弃。定义需要被丢弃的词法符号需要用skip指令通知词法分析器将它们丢弃。案例如下:
LINE_COMMENT : '//' .*? '\r'?'\n' -> skip ;	
COMMENT		 : '/*' .*? '*/'	  -> skip ; 

大多编程语言把空白字符当成词法符号间的分隔符,并可以将它们忽略,丢弃空白符的ANTLR语法如下:
WS : (' '|'\t'|'\r'|'\n')+ -> skip ;
或者
WS : [ \t\r\n]+ -> skip;

4. 小结

以上内容主要对ANTLR常见的一些语法进行了说明,后续会对更细节的语法进行说明

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值