ANTLR:设计语法

原创
2017/08/06 15:21
阅读数 1K

设计语法

语法中常用的模式

Sequence

USER parrt
PASS secret
RETR 1

一般顺序语法里面,还会有Key-Value的形式

retr : 'RETR' INT '\n' ; // match keyword integer newline sequence

在顺序语法中,列表是很常用的

INT+   #最少一个INT
INT*   #0个或多个INT
INT?   #0个或1个

CSV语法示例

file : (row '\n')* ; // sequence with a '\n' terminator 
row : field (',' field)* ; // sequence with a ',' separator 
field: INT ; // assume fields are just integers

Java语句换行示例

stats : (stat ';')* ; // match zero or more ';'-terminated statements

Choice

就是可以选择A或者B这样的语法

field : INT | STRING ;

更复杂一点的例子

stmt: node_stmt | 
	  edge_stmt | 
	  attr_stmt | 
	  id '=' id | 
	  subgraph ;

Token Dependency

一个非常复杂的例子,Token Dependency其实就是旁边那些括号和字符,例如底下这样的设计,因为有一些语法块在里面,就可以支持函数的层层嵌套了

/** Simple statically-typed programming language with functions and variables
 *  taken from "Language Implementation Patterns" book.
 */
grammar Cymbol;

file:   (functionDecl | varDecl)+ ;

varDecl
    :   type ID ('=' expr)? ';'
    ;
type:   'float' | 'int' | 'void' ; // user-defined types

functionDecl
    :   type ID '(' formalParameters? ')' block // "void f(int x) {...}"
    ;
formalParameters
    :   formalParameter (',' formalParameter)*
    ;
formalParameter
    :   type ID
    ;

block:  '{' stat* '}' ;   // possibly empty statement block
stat:   block
    |   varDecl
    |   'if' expr 'then' stat ('else' stat)?
    |   'return' expr? ';' 
    |   expr '=' expr ';' // assignment
    |   expr ';'          // func call
    ;

expr:   ID '(' exprList? ')'    // func call like f(), f(x), f(1,2)
    |   ID '[' expr ']'         // array index like a[i], a[i][j]
    |   '-' expr                // unary minus
    |   '!' expr                // boolean not
    |   expr '*' expr
    |   expr ('+'|'-') expr
    |   expr '==' expr          // equality comparison (lowest priority op)
    |   ID                      // variable reference
    |   INT
    |   '(' expr ')'
    ;
exprList : expr (',' expr)* ;   // arg list

ID  :   LETTER (LETTER | [0-9])* ;
fragment
LETTER : [a-zA-Z] ;

INT :   [0-9]+ ;

WS  :   [ \t\n\r]+ -> skip ;

SL_COMMENT
    :   '//' .*? '\n' -> skip
    ;

JSON语法示例

object
	: '{' pair (',' pair)* '}' 
	| 
	'{' '}' // empty object ;
pair: STRING ':' value ;

Nested Phrase

这种模式用于处理自引用的语法

stat: 'while' '(' expr ')' stat // match WHILE statement
	| '{' stat* '}' // match block of statements in curlies
	... // and other kinds of statements 
	;

优化后,把复合结构提取出来

stat: 'while' '(' expr ')' stat // match WHILE statement
	| block 	// match a block of statements
	...
	;

block: '{' stat* '}' ; // match block of statements in curlies

多层数组示例

expr: ID '[' expr ']' // a[1], a[b[1]], a[(2*b[1])]
	   | '(' expr ')' // (1), (a[1]), (((1))), (2*a[1]) 
	   | INT // 1, 94117
	;

处理左递归、优先级以及关联

以以下语法为例

expr : expr '*' expr // match subexpressions joined with '*' operator 
		| expr '+' expr // match subexpressions joined with '+' operator 
		| INT // matches simple integer atom
;

这种语法能够处理1+2*3这样的语句,但是有个问题,究竟怎么处理它的优先级呢?ANTLR在这种选择的情况下,会帮我们从第一条解析起,然后再回过头递归解析前面低优先级的

在ANTLR里面,默认是从左往右解析的,假如需要从右边解析起,那么可以使用

expr : expr '^'<assoc=right> expr // ^ operator is right associative 
	| INT
	;

识别通用的词法结构

匹配标识符

ID : ('a'..'z'|'A'..'Z')+ ; // match 1-or-more upper or lowercase letters
ID : [a-zA-Z]+ ;  //简写

当我们有一些自己的特殊字符的时候,将它放上面,那么优先级就会上升,就不会被后面的语句匹配到了

grammar KeywordTest;
enumDef : 'enum' '{' ... '}' ;
...
FOR : 'for' ;
...
ID : [a-zA-Z]+ ; // does NOT match 'enum' or 'for'

匹配数字

整数

INT : [0-9]+ ;

浮点值(在fragment底下的词,ANTLR不会生成对应的解析,只会在语法生成过程中内部使用)

FLOAT: DIGIT+ '.' DIGIT* // match 1. 39. 3.14159 etc... 
	| '.' DIGIT+ // match .1 .14159
;

fragment		
DIGIT : [0-9] ; // match single digit

匹配字符串

STRING : '"' .*? '"' ; 

让字符串支持特殊符号

STRING: '"' (ESC|.)*? '"' ;
fragment
ESC : '\\"' | '\\\\' ; // 2-char sequences \" and \\

匹配空格与注释

匹配注释

LINE_COMMENT : '//' .*? '\r'? '\n' -> skip ; // Match "//" stuff '\n' 
COMMENT : '/*' .*? '*/' -> skip ; // Match "/*" stuff "*/"

匹配空格

WS : [ \t\r\n]+ -> skip ; // match 1-or-more whitespace but discard

词法与解析器之间的界限

  • 匹配的动作放在文法里面去做,解析器就不用关心这部分的事情
  • 通用的词汇在文法里面设计
  • 把完整的语句在词法中识别了,然后才交给解析器
  • 把解析器不关心的内容在词法中设计好,这样解析器就不用看这些没有意义的内容了,例如XML格式
  • 尽可能在文法设计里面把词设计好,不用出现一大片的字符串交给后台处理
展开阅读全文
加载中

作者的其它热门文章

打赏
0
0 收藏
分享
打赏
0 评论
0 收藏
0
分享
返回顶部
顶部