设计语法
语法中常用的模式
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格式
- 尽可能在文法设计里面把词设计好,不用出现一大片的字符串交给后台处理