《ANTLR 4权威指南》笔记4 - 快速浏览
本章有5个例子
4.1 匹配算数表达式语言⌗
算数表达式如下所示:
193
a = 5
b = 6
a+b*2
(1+2)*3
语法文件:
grammar Expr;
prog: stat+ ;
stat: expr NEWLINE
| ID '=' expr NEWLINE
| NEWLINE
;
expr: expr ('*'|'/') expr
| expr ('+'|'-') expr
| INT
| ID
| '(' expr ')'
;
ID: [a-zA-Z]+ ;
INT: [0-9]+ ;
NEWLINE: '\r'? '\n' ;
WS: [ \t]+ -> skip ;
- 语法包括一些文法规则。有文法结构的规则比如
stat
和expr
,也有表示此法单元的规则比如ID
和INT
。 - 小写字母开头的是语法解析器规则。
- 大写字母开头的是词法规则,一般全部大写。
- 用竖线
|
表示多种可能的规则,也可以用括号表示子规则,比如('*'|'/')
ANTLR可以处理大多数的左递归文法。
分离词法和语法文件:
注意用lexer grammer
lexer grammar CommonLexerRules; // note "lexer grammar"
ID : [a-zA-Z]+ ;
INT : [0-9]+ ;
NEWLINE:'\r'? '\n' ;
WS : [ \t]+ -> skip ;
导入语法文件:
grammar LibExpr; // Rename to distinguish from original
import CommonLexerRules; // includes all rules from CommonLexerRules.g4
prog: stat+ ;
stat: expr NEWLINE
| ID '=' expr NEWLINE
| NEWLINE
;
expr: expr ('*'|'/') expr
| expr ('+'|'-') expr
| INT
| ID
| '(' expr ')'
;
4.2 用visitor构造计算器⌗
带标签的规则
grammar LabeledExpr; // rename to distinguish from Expr.g4
prog: stat+ ;
stat: expr NEWLINE # printExpr
| ID '=' expr NEWLINE # assign
| NEWLINE # blank
;
expr: expr op=('*'|'/') expr # MulDiv
| expr op=('+'|'-') expr # AddSub
| INT # int
| ID # id
| '(' expr ')' # parens
;
MUL : '*' ; // assigns token name to '*' used above in grammar
DIV : '/' ;
ADD : '+' ;
SUB : '-' ;
ID : [a-zA-Z]+ ; // match identifiers
INT : [0-9]+ ; // match integers
NEWLINE:'\r'? '\n' ; // return newlines to parser (is end-statement signal)
WS : [ \t]+ -> skip ; // toss out whitespace
普通规则的标签写在#
后面,子规则的标签用tagName=()
。
用Listener构造翻译器⌗
要求解析文件:
import java.util.List;
import java.util.Map;
public class Demo {
void f(int x, String y) { }
int[ ] g(/*no args*/) { return null; }
List<Map<String, Integer>>[] h() { return null; }
}
生成接口文件:
interface IDemo {
void f(int x, String y);
int[ ] g(/*no args*/);
List<Map<String, Integer>>[] h();
}
用Listener:
import org.antlr.v4.runtime.TokenStream;
import org.antlr.v4.runtime.misc.Interval;
public class ExtractInterfaceListener extends JavaBaseListener {
JavaParser parser;
public ExtractInterfaceListener(JavaParser parser) {this.parser = parser;}
/** Listen to matches of classDeclaration */
@Override
public void enterClassDeclaration(JavaParser.ClassDeclarationContext ctx){
System.out.println("interface I"+ctx.Identifier()+" {");
}
@Override
public void exitClassDeclaration(JavaParser.ClassDeclarationContext ctx) {
System.out.println("}");
}
/** Listen to matches of methodDeclaration */
@Override
public void enterMethodDeclaration(
JavaParser.MethodDeclarationContext ctx
)
{
// need parser to get tokens
TokenStream tokens = parser.getTokenStream();
String type = "void";
if ( ctx.type()!=null ) {
type = tokens.getText(ctx.type());
}
String args = tokens.getText(ctx.formalParameters());
System.out.println("\t"+type+" "+ctx.Id=entifier()+args+";");
}
}
在语法中嵌入任意动作⌗
需要处理的文件:
parrt Terence Parr 101
tombu Tom Burns 020
bke Kevin Edgar 008
简易的语法文件:
file : (row NL)+ ; // NL is newline token: '\r'? '\n'
row : STUFF+ ;
完整的语法文件:
grammar Rows;
@parser::members { // add members to generated RowsParser
int col;
public RowsParser(TokenStream input, int col) { // custom constructor
this(input);
this.col = col;
}
}
file: (row NL)+ ;
row
locals [int i=0]
: ( STUFF
{
$i++;
if ( $i == col ) System.out.println($STUFF.text);
}
)+
;
TAB : '\t' -> skip ; // match but don't pass to the parser
NL : '\r'? '\n' ; // match and pass to the parser
STUFF: ~[\t\r\n]+ ; // match any chars except tab, newline
用语义谓词更改解析逻辑⌗
假设输入:
2 9 10 3 1 2 3
需要把输入的整数分组,比如:
(group 2 (sequence 9 10)) (group 3 (sequence 1 2 3))
用以下语法文件:
grammar Data;
file : group+ ;
group: INT sequence[$INT.int] ;
sequence[int n]
locals [int i = 1;]
: ( {$i<=$n}? INT {$i++;} )* // match n integers
;
INT : [0-9]+ ; // match integers
WS : [ \t\n\r]+ -> skip ; // toss out all whitespace
4.5 酷炫的词法特性⌗
ANTLR有3种token相关的特点
同时解析多种语言的文件⌗
岛屿语法:比如Python文件中嵌入的Django模板
ANTLR提供词法分析特性lexical modes用来处理混合的格式
用于分析XML的语法文件:
lexer grammar XMLLexer;
// Default "mode": Everything OUTSIDE of a tag
OPEN : '<' -> pushMode(INSIDE) ;
COMMENT : '<!--' .*? '-->' -> skip ;
EntityRef : '&' [a-z]+ ';' ;
TEXT : ~('<'|'&')+ ; // match any 16 bit char minus < and &
// ----------------- Everything INSIDE of a tag ---------------------
mode INSIDE;
CLOSE : '>' -> popMode ; // back to default mode
SLASH_CLOSE : '/>' -> popMode ;
EQUALS : '=' ;
STRING : '"' .*? '"' ;
SlashName : '/' Name ;
Name : ALPHA (ALPHA|DIGIT)* ;
S : [ \t\r\n] -> skip ;
fragment
ALPHA : [a-zA-Z] ;
fragment
DIGIT : [0-9] ;
XML文件:
<tools>
<tool name="ANTLR">A parser generator</tool>
</tools>
重写输入流⌗
略
发送token到不同的通道⌗
略