Antlr4使用教程


Antlr是一款强大的语法分析器生成工具,可用于读取、处理、执行和翻译结构化的文本或二进制文件。它被广泛用于学术领域和工业生产领域,是众多语言、工具和框架的基石。

安装及配置(unix)

安装

可直接下载jar包或用命令行安装

1
2
3
$ cd /usr/local/lib

$ curl -O http://www.antlr.org/download/antlr-4.7.1-complete.jar

配置

1
2
3
4
5
6
7
#!~/.bash_profile

$ export CLASSPATH=".:/usr/local/lib/antlr-4.0-complete.jar:$CLASSPATH"

$ alias antlr4='java -jar /usr/local/lib/antlr-4.7.1-complete.jar'

$ alias grun='java org.antlr.v4.gui.TestRig'

验证

1
2
3
$ antlr4

$ grun

一些概念

词法和语法

  • 语言:一门语言是一个有效语句的集合。语句由词组组成,子词组又有更小的子词组组成,依次类推。
  • 语法:语法定义了语言的语义规则。语法中的每条规则定义了一种词组结构。
  • 语义树或语法分析树:代表了语句的结构,其中的每个子树的根节点都使用一个抽象的名字给其包含的元素命名。即字子树的根节点对应了语法规则的名字。树的叶子节点是语句中的符号或词法符号。
  • 词法符号:是一门语言的基本词汇符号,它们可以代表像是“标识符”这样的一类符号,也可以代表一个单一的运算符,或者代表一个关键字。
  • 词法分析器或者词法符号生成器:将输入的字符序列分解成一系列词法符号。一个此法分析器负责分析词法。
  • 语法分析器:语法分析器通过检查语句的结构是否符合语法规则的定义来验证该语句在特定语言中是否合法。语法分析的过程好比是走迷宫,通过比较语句中的地板上的单词来从入口到出口。ANTLR能够生成被称为ALL()的自顶向下的语法分析器。ALL()是指它可以利用剩余的所有输入文本来进行决策。自顶向下的语法分析器以结果为导向,首先匹配最粗粒度的规则。

  • 词法符号比如INT、STRING、ID等基本的token,词法分析(lexical analysis)又叫词法符号化(tokenizing)。
  • 在语法分析的过程会建立语法分析树或叫句法树,语法分析树的内部节点是词组名,子根节点表达的是语法文件中的规则(rule),也叫做rulenode,也叫做context对象,叶子节点永远是输入的词法符号,也叫做terminalnode。

语法分析树监听器和访问器

项目实战

chap3

ArrayInit

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/** Grammars always start with a grammar header. This grammar is called
* ArrayInit and must match the filename: ArrayInit.g4
*/
grammar ArrayInit;

/** A rule called init that matches comma-separated values between {...}. */
init : '{' value (',' value)* '}' ; // must match at least one value

/** A value can be either a nested array/struct or a simple integer (INT) */
value : init
| INT
;

// parser rules start with lowercase letters, lexer rules with uppercase
INT : [0-9]+ ; // Define token INT as one or more digits
WS : [ \t\r\n]+ -> skip ; // Define whitespace rule, toss it out
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
$ antlr4 ArrayInit.g4 # Generate parser and lexer using antlr4 alias

$ javac ArrayInit*.java # Compile ANTLR-generated code

➾ $ grun ArrayInit init -tokens
➾ {99, 3, 451}
➾EOF
❮ [@0,0:0='{',<1>,1:0]
[@1,1:2='99',<4>,1:1]
[@2,3:3=',',<2>,1:3]
[@3,5:5='3',<4>,1:5]
[@4,6:6=',',<2>,1:6]
[@5,8:10='451',<4>,1:8]
[@6,11:11='}',<3>,1:11]
[@7,13:12='<EOF>',<-1>,2:0]

➾ $ grun ArrayInit init -tree
➾ {99, 3, 451}
➾EOF
❮ (init { (value 99) , (value 3) , (value 451) })

➾ $ grun ArrayInit init -gui
➾ {1,{2,3},4}
➾EOF

chap4

Expr

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
grammar Expr;

/** The start rule; begin parsing here. */
prog: stat+ ;

stat: expr NEWLINE
| ID '=' expr NEWLINE
| NEWLINE
;

expr: INT
| ID
| '(' expr ')'
| expr ('*'|'/') expr
| expr ('+'|'-') expr
;

ID : [a-zA-Z]+ ; // match identifiers <label id="code.tour.expr.3"/>
INT : [0-9]+ ; // match integers
NEWLINE:'\r'? '\n' ; // return newlines to parser (is end-statement signal)
WS : [ \t]+ -> skip ; // toss out whitespace
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
//: ExprJoyRide.java
/***
* Excerpted from "The Definitive ANTLR 4 Reference",
* published by The Pragmatic Bookshelf.
* Copyrights apply to this code. It may not be used to create training material,
* courses, books, articles, and the like. Contact us if you are in doubt.
* We make no guarantees that this code is fit for any purpose.
* Visit http://www.pragmaticprogrammer.com/titles/tpantlr2 for more book information.
***/
import org.antlr.v4.runtime.*;
import org.antlr.v4.runtime.tree.*;
import java.io.FileInputStream;
import java.io.InputStream;
public class ExprJoyRide {
public static void main(String[] args) throws Exception {
String inputFile = null;
if ( args.length>0 ) inputFile = args[0];
InputStream is = System.in;
if ( inputFile!=null ) is = new FileInputStream(inputFile);
ANTLRInputStream input = new ANTLRInputStream(is);
ExprLexer lexer = new ExprLexer(input);
CommonTokenStream tokens = new CommonTokenStream(lexer);
ExprParser parser = new ExprParser(tokens);
ParseTree tree = parser.prog(); // parse; start at prog <label id="code.tour.main.6"/>
System.out.println(tree.toStringTree(parser)); // print tree as text <label id="code.tour.main.7"/>
}
}
1
2
3
4
5
193
a = 5
b = 6
a+b*2
(1+2)*3
1
2
3
4
5
6
7
8
9
10
11
12
13
14
$ antlr4 Expr.g4

$ javac Expr*.java ExprJoyRide.java

$ grun Expr prog -gui t.expr # launches org.antlr.v4.runtime.misc.TestRig

$ java ExprJoyRide test
❮ (prog
(stat (expr 193) \n)
(stat a = (expr 5) \n)
(stat b = (expr 6) \n)
(stat (expr (expr a) + (expr (expr b) * (expr 2))) \n)
(stat (expr (expr ( (expr (expr 1) + (expr 2)) )) * (expr 3)) \n)
)

LibExpr

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// CommonLexerRules.g4
lexer grammar CommonLexerRules; // note "lexer grammar"

ID : [a-zA-Z]+ ; // match identifiers
INT : [0-9]+ ; // match integers
NEWLINE:'\r'? '\n' ; // return newlines to parser (end-statement signal)
WS : [ \t]+ -> skip ; // toss out whitespace

// LibExpr.g4
grammar LibExpr; // Rename to distinguish from original
import CommonLexerRules; // includes all rules from CommonLexerRules.g4
/** The start rule; begin parsing here. */
prog: stat+ ;

stat: expr NEWLINE
| ID '=' expr NEWLINE
| NEWLINE
;

expr: expr ('*'|'/') expr
| expr ('+'|'-') expr
| INT
| ID
| '(' expr ')'
;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
//: ExprJoyRide.java
/***
* Excerpted from "The Definitive ANTLR 4 Reference",
* published by The Pragmatic Bookshelf.
* Copyrights apply to this code. It may not be used to create training material,
* courses, books, articles, and the like. Contact us if you are in doubt.
* We make no guarantees that this code is fit for any purpose.
* Visit http://www.pragmaticprogrammer.com/titles/tpantlr2 for more book information.
***/
import org.antlr.v4.runtime.*;
import org.antlr.v4.runtime.tree.*;
import java.io.FileInputStream;
import java.io.InputStream;
public class ExprJoyRide {
public static void main(String[] args) throws Exception {
String inputFile = null;
if ( args.length>0 ) inputFile = args[0];
InputStream is = System.in;
if ( inputFile!=null ) is = new FileInputStream(inputFile);
ANTLRInputStream input = new ANTLRInputStream(is);
LibExprLexer lexer = new LibExprLexer(input);
CommonTokenStream tokens = new CommonTokenStream(lexer);
LibExprParser parser = new LibExprParser(tokens);
ParseTree tree = parser.prog(); // parse; start at prog <label id="code.tour.main.6"/>
System.out.println(tree.toStringTree(parser)); // print tree as text <label id="code.tour.main.7"/>
}
}

LabeledExpr

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
//: EvalVisitor.java
/***
* Excerpted from "The Definitive ANTLR 4 Reference",
* published by The Pragmatic Bookshelf.
* Copyrights apply to this code. It may not be used to create training material,
* courses, books, articles, and the like. Contact us if you are in doubt.
* We make no guarantees that this code is fit for any purpose.
* Visit http://www.pragmaticprogrammer.com/titles/tpantlr2 for more book information.
***/
import java.util.HashMap;
import java.util.Map;

public class EvalVisitor extends LabeledExprBaseVisitor<Integer> {
/** "memory" for our calculator; variable/value pairs go here */
Map<String, Integer> memory = new HashMap<String, Integer>();

/** ID '=' expr NEWLINE */
@Override
public Integer visitAssign(LabeledExprParser.AssignContext ctx) {
String id = ctx.ID().getText(); // id is left-hand side of '='
int value = visit(ctx.expr()); // compute value of expression on right
memory.put(id, value); // store it in our memory
return value;
}

/** expr NEWLINE */
@Override
public Integer visitPrintExpr(LabeledExprParser.PrintExprContext ctx) {
Integer value = visit(ctx.expr()); // evaluate the expr child
System.out.println(value); // print the result
return 0; // return dummy value
}

/** INT */
@Override
public Integer visitInt(LabeledExprParser.IntContext ctx) {
return Integer.valueOf(ctx.INT().getText());
}

/** ID */
@Override
public Integer visitId(LabeledExprParser.IdContext ctx) {
String id = ctx.ID().getText();
if ( memory.containsKey(id) ) return memory.get(id);
return 0;
}

/** expr op=('*'|'/') expr */
@Override
public Integer visitMulDiv(LabeledExprParser.MulDivContext ctx) {
int left = visit(ctx.expr(0)); // get value of left subexpression
int right = visit(ctx.expr(1)); // get value of right subexpression
if ( ctx.op.getType() == LabeledExprParser.MUL ) return left * right;
return left / right; // must be DIV
}

/** expr op=('+'|'-') expr */
@Override
public Integer visitAddSub(LabeledExprParser.AddSubContext ctx) {
int left = visit(ctx.expr(0)); // get value of left subexpression
int right = visit(ctx.expr(1)); // get value of right subexpression
if ( ctx.op.getType() == LabeledExprParser.ADD ) return left + right;
return left - right; // must be SUB
}

/** '(' expr ')' */
@Override
public Integer visitParens(LabeledExprParser.ParensContext ctx) {
return visit(ctx.expr()); // return child expr's value
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
//: Calc.java
/***
* Excerpted from "The Definitive ANTLR 4 Reference",
* published by The Pragmatic Bookshelf.
* Copyrights apply to this code. It may not be used to create training material,
* courses, books, articles, and the like. Contact us if you are in doubt.
* We make no guarantees that this code is fit for any purpose.
* Visit http://www.pragmaticprogrammer.com/titles/tpantlr2 for more book information.
***/
import org.antlr.v4.runtime.*;
import org.antlr.v4.runtime.tree.ParseTree;

import java.io.FileInputStream;
import java.io.InputStream;

public class Calc {
public static void main(String[] args) throws Exception {
String inputFile = null;
if ( args.length>0 ) inputFile = args[0];
InputStream is = System.in;
if ( inputFile!=null ) is = new FileInputStream(inputFile);
ANTLRInputStream input = new ANTLRInputStream(is);
LabeledExprLexer lexer = new LabeledExprLexer(input);
CommonTokenStream tokens = new CommonTokenStream(lexer);
LabeledExprParser parser = new LabeledExprParser(tokens);
ParseTree tree = parser.prog(); // parse

EvalVisitor eval = new EvalVisitor();
eval.visit(tree);
}
}
1
2
3
4
5
193
a = 5
b = 6
a+b*2
(1+2)*3
1
2
3
4
5
6
$ java Calc test
193
5
6
17
9

ExtractJava

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
// Java.g4
/** Java 1.6 grammar (ANTLR v4). Derived from

http://docs.oracle.com/javase/specs/jls/se7/jls7.pdf

and JavaParser.g from ANTLR v3
*/
grammar Java;

@lexer::members {
protected boolean enumIsKeyword = true;
protected boolean assertIsKeyword = true;
}

// starting point for parsing a java file
compilationUnit
: packageDeclaration? importDeclaration* typeDeclaration*
EOF
;

packageDeclaration
: 'package' qualifiedName ';'
;

importDeclaration
: 'import' 'static'? qualifiedName ('.' '*')? ';'
;

typeDeclaration
: classOrInterfaceModifier*
( classDeclaration
| interfaceDeclaration
| enumDeclaration
)
| ';'
;

classDeclaration
: 'class' Identifier typeParameters? ('extends' type)?
('implements' typeList)?
classBody
;

enumDeclaration
: ENUM Identifier ('implements' typeList)? enumBody
;

interfaceDeclaration
: normalInterfaceDeclaration
| annotationTypeDeclaration
;

classOrInterfaceModifier
: annotation // class or interface
| 'public' // class or interface
| 'protected' // class or interface
| 'private' // class or interface
| 'abstract' // class or interface
| 'static' // class or interface
| 'final' // class only -- does not apply to interfaces
| 'strictfp' // class or interface
;

modifiers
: modifier*
;

typeParameters
: '<' typeParameter (',' typeParameter)* '>'
;

typeParameter
: Identifier ('extends' typeBound)?
;

typeBound
: type ('&' type)*
;

enumBody
: '{' enumConstants? ','? enumBodyDeclarations? '}'
;

enumConstants
: enumConstant (',' enumConstant)*
;

enumConstant
: annotations? Identifier arguments? classBody?
;

enumBodyDeclarations
: ';' (classBodyDeclaration)*
;

normalInterfaceDeclaration
: 'interface' Identifier typeParameters? ('extends' typeList)? interfaceBody
;

typeList
: type (',' type)*
;

classBody
: '{' classBodyDeclaration* '}'
;

interfaceBody
: '{' interfaceBodyDeclaration* '}'
;

classBodyDeclaration
: ';'
| 'static'? block
| modifiers member
;

member
: genericMethodDeclaration
| methodDeclaration
| fieldDeclaration
| constructorDeclaration
| interfaceDeclaration
| classDeclaration
;

methodDeclaration
: type Identifier formalParameters ('[' ']')* methodDeclarationRest
| 'void' Identifier formalParameters methodDeclarationRest
;

methodDeclarationRest
: ('throws' qualifiedNameList)?
( methodBody
| ';'
)
;

genericMethodDeclaration
: typeParameters methodDeclaration
;

fieldDeclaration
: type variableDeclarators ';'
;

constructorDeclaration
: typeParameters? Identifier formalParameters
('throws' qualifiedNameList)? constructorBody
;

interfaceBodyDeclaration
: modifiers interfaceMemberDecl
| ';'
;

interfaceMemberDecl
: interfaceMethodOrFieldDecl
| interfaceGenericMethodDecl
| 'void' Identifier voidInterfaceMethodDeclaratorRest
| interfaceDeclaration
| classDeclaration
;

interfaceMethodOrFieldDecl
: type Identifier interfaceMethodOrFieldRest
;

interfaceMethodOrFieldRest
: constantDeclaratorsRest ';'
| interfaceMethodDeclaratorRest
;

voidMethodDeclaratorRest
: formalParameters ('throws' qualifiedNameList)?
( methodBody
| ';'
)
;

interfaceMethodDeclaratorRest
: formalParameters ('[' ']')* ('throws' qualifiedNameList)? ';'
;

interfaceGenericMethodDecl
: typeParameters (type | 'void') Identifier
interfaceMethodDeclaratorRest
;

voidInterfaceMethodDeclaratorRest
: formalParameters ('throws' qualifiedNameList)? ';'
;

constantDeclarator
: Identifier constantDeclaratorRest
;

variableDeclarators
: variableDeclarator (',' variableDeclarator)*
;

variableDeclarator
: variableDeclaratorId ('=' variableInitializer)?
;

constantDeclaratorsRest
: constantDeclaratorRest (',' constantDeclarator)*
;

constantDeclaratorRest
: ('[' ']')* '=' variableInitializer
;

variableDeclaratorId
: Identifier ('[' ']')*
;

variableInitializer
: arrayInitializer
| expression
;

arrayInitializer
: '{' (variableInitializer (',' variableInitializer)* (',')? )? '}'
;

modifier
: annotation
| 'public'
| 'protected'
| 'private'
| 'static'
| 'abstract'
| 'final'
| 'native'
| 'synchronized'
| 'transient'
| 'volatile'
| 'strictfp'
;

packageOrTypeName
: qualifiedName
;

enumConstantName
: Identifier
;

typeName
: qualifiedName
;

type: classOrInterfaceType ('[' ']')*
| primitiveType ('[' ']')*
;

classOrInterfaceType
: Identifier typeArguments? ('.' Identifier typeArguments? )*
;

primitiveType
: 'boolean'
| 'char'
| 'byte'
| 'short'
| 'int'
| 'long'
| 'float'
| 'double'
;

variableModifier
: 'final'
| annotation
;

typeArguments
: '<' typeArgument (',' typeArgument)* '>'
;

typeArgument
: type
| '?' (('extends' | 'super') type)?
;

qualifiedNameList
: qualifiedName (',' qualifiedName)*
;

formalParameters
: '(' formalParameterDecls? ')'
;

formalParameterDecls
: variableModifiers type formalParameterDeclsRest
;

formalParameterDeclsRest
: variableDeclaratorId (',' formalParameterDecls)?
| '...' variableDeclaratorId
;

methodBody
: block
;

constructorBody
: '{' explicitConstructorInvocation? blockStatement* '}'
;

explicitConstructorInvocation
: nonWildcardTypeArguments? ('this' | 'super') arguments ';'
| primary '.' nonWildcardTypeArguments? 'super' arguments ';'
;

qualifiedName
: Identifier ('.' Identifier)*
;

literal
: integerLiteral
| FloatingPointLiteral
| CharacterLiteral
| StringLiteral
| booleanLiteral
| 'null'
;

integerLiteral
: HexLiteral
| OctalLiteral
| DecimalLiteral
;

booleanLiteral
: 'true'
| 'false'
;

// ANNOTATIONS

annotations
: annotation+
;

annotation
: '@' annotationName ( '(' ( elementValuePairs | elementValue )? ')' )?
;

annotationName
: Identifier ('.' Identifier)*
;

elementValuePairs
: elementValuePair (',' elementValuePair)*
;

elementValuePair
: Identifier '=' elementValue
;

elementValue
: expression
| annotation
| elementValueArrayInitializer
;

elementValueArrayInitializer
: '{' (elementValue (',' elementValue)*)? (',')? '}'
;

annotationTypeDeclaration
: '@' 'interface' Identifier annotationTypeBody
;

annotationTypeBody
: '{' (annotationTypeElementDeclaration)* '}'
;

annotationTypeElementDeclaration
: modifiers annotationTypeElementRest
;

annotationTypeElementRest
: type annotationMethodOrConstantRest ';'
| classDeclaration ';'?
| normalInterfaceDeclaration ';'?
| enumDeclaration ';'?
| annotationTypeDeclaration ';'?
;

annotationMethodOrConstantRest
: annotationMethodRest
| annotationConstantRest
;

annotationMethodRest
: Identifier '(' ')' defaultValue?
;

annotationConstantRest
: variableDeclarators
;

defaultValue
: 'default' elementValue
;

// STATEMENTS / BLOCKS

block
: '{' blockStatement* '}'
;

blockStatement
: localVariableDeclarationStatement
| classDeclaration
| interfaceDeclaration
| statement
;

localVariableDeclarationStatement
: localVariableDeclaration ';'
;

localVariableDeclaration
: variableModifiers type variableDeclarators
;

variableModifiers
: variableModifier*
;

statement
: block
| ASSERT expression (':' expression)? ';'
| 'if' parExpression statement ('else' statement)?
| 'for' '(' forControl ')' statement
| 'while' parExpression statement
| 'do' statement 'while' parExpression ';'
| 'try' block
( catches 'finally' block
| catches
| 'finally' block
)
| 'switch' parExpression switchBlock
| 'synchronized' parExpression block
| 'return' expression? ';'
| 'throw' expression ';'
| 'break' Identifier? ';'
| 'continue' Identifier? ';'
| ';'
| statementExpression ';'
| Identifier ':' statement
;

catches
: catchClause (catchClause)*
;

catchClause
: 'catch' '(' formalParameter ')' block
;

formalParameter
: variableModifiers type variableDeclaratorId
;

switchBlock
: '{' switchBlockStatementGroup* switchLabel* '}'
;

switchBlockStatementGroup
: switchLabel+ blockStatement*
;

switchLabel
: 'case' constantExpression ':'
| 'case' enumConstantName ':'
| 'default' ':'
;

forControl
: enhancedForControl
| forInit? ';' expression? ';' forUpdate?
;

forInit
: localVariableDeclaration
| expressionList
;

enhancedForControl
: variableModifiers type Identifier ':' expression
;

forUpdate
: expressionList
;

// EXPRESSIONS

parExpression
: '(' expression ')'
;

expressionList
: expression (',' expression)*
;

statementExpression
: expression
;

constantExpression
: expression
;

expression
: primary
| expression '.' Identifier
| expression '.' 'this'
| expression '.' 'super' '(' expressionList? ')'
| expression '.' 'new' Identifier '(' expressionList? ')'
| expression '.' 'super' '.' Identifier arguments?
| expression '.' explicitGenericInvocation
| expression '[' expression ']'
| expression '(' expressionList? ')'
| expression ('++' | '--')
| ('+'|'-'|'++'|'--') expression
| ('~'|'!') expression
| '(' type ')' expression
| 'new' creator
| expression ('*'|'/'|'%') expression
| expression ('+'|'-') expression
| expression ('<' '<' | '>' '>' '>' | '>' '>') expression
| expression ('<' '=' | '>' '=' | '>' | '<') expression
| expression 'instanceof' type
| expression ('==' | '!=') expression
| expression '&' expression
| expression '^' expression
| expression '|' expression
| expression '&&' expression
| expression '||' expression
| expression '?' expression ':' expression
| expression
('^='<assoc=right>
|'+='<assoc=right>
|'-='<assoc=right>
|'*='<assoc=right>
|'/='<assoc=right>
|'&='<assoc=right>
|'|='<assoc=right>
|'='<assoc=right>
|'>' '>' '='<assoc=right>
|'>' '>' '>' '='<assoc=right>
|'<' '<' '='<assoc=right>
|'%='<assoc=right>
)
expression
;

primary
: '(' expression ')'
| 'this'
| 'super'
| literal
| Identifier
| type '.' 'class'
| 'void' '.' 'class'
;

creator
: nonWildcardTypeArguments createdName classCreatorRest
| createdName (arrayCreatorRest | classCreatorRest)
;

createdName
: classOrInterfaceType
| primitiveType
;

innerCreator
: nonWildcardTypeArguments? Identifier classCreatorRest
;

explicitGenericInvocation
: nonWildcardTypeArguments Identifier arguments
;

arrayCreatorRest
: '['
( ']' ('[' ']')* arrayInitializer
| expression ']' ('[' expression ']')* ('[' ']')*
)
;

classCreatorRest
: arguments classBody?
;

nonWildcardTypeArguments
: '<' typeList '>'
;

arguments
: '(' expressionList? ')'
;

// LEXER

HexLiteral : '0' ('x'|'X') HexDigit+ IntegerTypeSuffix? ;

DecimalLiteral : ('0' | '1'..'9' '0'..'9'*) IntegerTypeSuffix? ;

OctalLiteral : '0' ('0'..'7')+ IntegerTypeSuffix? ;

fragment
HexDigit : ('0'..'9'|'a'..'f'|'A'..'F') ;

fragment
IntegerTypeSuffix : ('l'|'L') ;

FloatingPointLiteral
: ('0'..'9')+ '.' ('0'..'9')* Exponent? FloatTypeSuffix?
| '.' ('0'..'9')+ Exponent? FloatTypeSuffix?
| ('0'..'9')+ Exponent FloatTypeSuffix?
| ('0'..'9')+ FloatTypeSuffix
| ('0x' | '0X') (HexDigit )*
('.' (HexDigit)*)?
( 'p' | 'P' )
( '+' | '-' )?
( '0' .. '9' )+
FloatTypeSuffix?
;

fragment
Exponent : ('e'|'E') ('+'|'-')? ('0'..'9')+ ;

fragment
FloatTypeSuffix : ('f'|'F'|'d'|'D') ;

CharacterLiteral
: '\'' ( EscapeSequence | ~('\''|'\\') ) '\''
;

StringLiteral
: '"' ( EscapeSequence | ~('\\'|'"') )* '"'
;

fragment
EscapeSequence
: '\\' ('b'|'t'|'n'|'f'|'r'|'\"'|'\''|'\\')
| UnicodeEscape
| OctalEscape
;

fragment
OctalEscape
: '\\' ('0'..'3') ('0'..'7') ('0'..'7')
| '\\' ('0'..'7') ('0'..'7')
| '\\' ('0'..'7')
;

fragment
UnicodeEscape
: '\\' 'u' HexDigit HexDigit HexDigit HexDigit
;

ENUM: 'enum' {if (!enumIsKeyword) setType(Identifier);}
;

ASSERT
: 'assert' {if (!assertIsKeyword) setType(Identifier);}
;

Identifier
: Letter (Letter|JavaIDDigit)*
;

/**I found this char range in JavaCC's grammar, but Letter and Digit overlap.
Still works, but...
*/
fragment
Letter
: '\u0024' |
'\u0041'..'\u005a' |
'\u005f' |
'\u0061'..'\u007a' |
'\u00c0'..'\u00d6' |
'\u00d8'..'\u00f6' |
'\u00f8'..'\u00ff' |
'\u0100'..'\u1fff' |
'\u3040'..'\u318f' |
'\u3300'..'\u337f' |
'\u3400'..'\u3d2d' |
'\u4e00'..'\u9fff' |
'\uf900'..'\ufaff'
;

fragment
JavaIDDigit
: '\u0030'..'\u0039' |
'\u0660'..'\u0669' |
'\u06f0'..'\u06f9' |
'\u0966'..'\u096f' |
'\u09e6'..'\u09ef' |
'\u0a66'..'\u0a6f' |
'\u0ae6'..'\u0aef' |
'\u0b66'..'\u0b6f' |
'\u0be7'..'\u0bef' |
'\u0c66'..'\u0c6f' |
'\u0ce6'..'\u0cef' |
'\u0d66'..'\u0d6f' |
'\u0e50'..'\u0e59' |
'\u0ed0'..'\u0ed9' |
'\u1040'..'\u1049'
;

COMMENT
: '/*' .*? '*/' -> channel(HIDDEN) // match anything between /* and */
;
WS : [ \r\t\u000C\n]+ -> channel(HIDDEN)
;

LINE_COMMENT
: '//' ~[\r\n]* '\r'? '\n' -> channel(HIDDEN)
;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
//: ExtractInterfaceListener.java
/***
* Excerpted from "The Definitive ANTLR 4 Reference",
* published by The Pragmatic Bookshelf.
* Copyrights apply to this code. It may not be used to create training material,
* courses, books, articles, and the like. Contact us if you are in doubt.
* We make no guarantees that this code is fit for any purpose.
* Visit http://www.pragmaticprogrammer.com/titles/tpantlr2 for more book information.
***/
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.Identifier()+args+";");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
//: ExtractInterfaceTool.java
/***
* Excerpted from "The Definitive ANTLR 4 Reference",
* published by The Pragmatic Bookshelf.
* Copyrights apply to this code. It may not be used to create training material,
* courses, books, articles, and the like. Contact us if you are in doubt.
* We make no guarantees that this code is fit for any purpose.
* Visit http://www.pragmaticprogrammer.com/titles/tpantlr2 for more book information.
***/
import org.antlr.v4.runtime.ANTLRInputStream;
import org.antlr.v4.runtime.CommonTokenStream;
import org.antlr.v4.runtime.ParserRuleContext;
import org.antlr.v4.runtime.Token;
import org.antlr.v4.runtime.tree.*;

import java.io.FileInputStream;
import java.io.InputStream;

public class ExtractInterfaceTool {
public static void main(String[] args) throws Exception {
String inputFile = null;
if ( args.length>0 ) inputFile = args[0];
InputStream is = System.in;
if ( inputFile!=null ) {
is = new FileInputStream(inputFile);
}
ANTLRInputStream input = new ANTLRInputStream(is);

JavaLexer lexer = new JavaLexer(input);
CommonTokenStream tokens = new CommonTokenStream(lexer);
JavaParser parser = new JavaParser(tokens);
ParseTree tree = parser.compilationUnit(); // parse

ParseTreeWalker walker = new ParseTreeWalker(); // create standard walker
ExtractInterfaceListener extractor = new ExtractInterfaceListener(parser);
walker.walk(extractor, tree); // initiate walk of tree with listener
}
}
1
2
3
4
5
6
7
8
9
10
11
//: Demo.java

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; }
}
1
2
3
4
5
6
$ java ExtractInterfaceTool Demo.java
interface IDemo{
void f(int x, String y);
int[ ] g(/*no args*/);
List<Map<String, Integer>>[] h();
}

Rows

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
//: Col.java
/***
* Excerpted from "The Definitive ANTLR 4 Reference",
* published by The Pragmatic Bookshelf.
* Copyrights apply to this code. It may not be used to create training material,
* courses, books, articles, and the like. Contact us if you are in doubt.
* We make no guarantees that this code is fit for any purpose.
* Visit http://www.pragmaticprogrammer.com/titles/tpantlr2 for more book information.
***/
import org.antlr.v4.runtime.ANTLRInputStream;
import org.antlr.v4.runtime.CommonTokenStream;
import org.antlr.v4.runtime.ParserRuleContext;
import org.antlr.v4.runtime.Token;

import java.io.FileInputStream;
import java.io.InputStream;

public class Col {
public static void main(String[] args) throws Exception {
ANTLRInputStream input = new ANTLRInputStream(System.in);
RowsLexer lexer = new RowsLexer(input);
CommonTokenStream tokens = new CommonTokenStream(lexer);
int col = Integer.valueOf(args[0]);
RowsParser parser = new RowsParser(tokens, col); // pass column number!
parser.setBuildParseTree(false); // don't waste time bulding a tree
parser.file(); // parse
}
}
1
2
3
parrt   Terence Parr    101
tombu Tom Burns 020
bke Kevin Edgar 008

chap6

CSV

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// CSV.g4
grammar CSV;

file : hdr row+ ;

hdr : row ;

row : field (',' field)* '\r'? '\n' ;

field : TEXT
| STRING
|
;

TEXT : ~[,\n\r"]+ ;

STRING: '"' ('""'|~'"')* '"' ;
1
2
3
4
Details,Month,Amount
Mid Bonus,June,"$2,000"
,January,"""zippo"""
Total Bonuses,"","$5,000"

JSON

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
// JSON.g4
// Derived from http://json.org

grammar JSON;

json: object
| array
;

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

array
: '[' value (',' value)* ']'
| '[' ']' // empty array
;

value
: STRING
| NUMBER
| object // recursion
| array // recursion
| 'true' // keywords
| 'false'
| 'null'
;

STRING : '"' (ESC | ~["\\])* '"' ;

fragment ESC : '\\' (["\\/bfnrt] | UNICODE) ;
fragment UNICODE : 'u' HEX HEX HEX HEX ;
fragment HEX : [0-9a-fA-F] ;

NUMBER
: '-'? INT '.' [0-9]+ EXP? // 1.35, 1.35E-9, 0.3, -4.5
| '-'? INT EXP // 1e10 -3e4
| '-'? INT // -3, 45
;
fragment INT : '0' | [1-9] [0-9]* ; // no leading zeros
fragment EXP : [Ee] [+\-]? INT ; // \- since - means "range" inside [...]

WS : [ \t\n\r]+ -> skip ;
1
2
3
4
5
6
7
8
{
"antlr.org": {
"owners" : [],
"live" : true,
"speed" : 145,
"menus" : ["File", "Help\nMenu"]
}
}

chap7

PropertyFile

1
2
3
4
5
6
7
8
9
10
grammar PropertyFile;
@members {
void startFile() { } // blank implementations
void finishFile() { }
void defineProperty(Token name, Token value) { }
}
file : {startFile();} prop+ {finishFile();} ;
prop : ID '=' STRING '\n' {defineProperty($ID, $STRING);} ;
ID : [a-z]+ ;
STRING : '"' .*? '"' ;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
//: PropertyFileRide.java
/***
* Excerpted from "The Definitive ANTLR 4 Reference",
* published by The Pragmatic Bookshelf.
* Copyrights apply to this code. It may not be used to create training material,
* courses, books, articles, and the like. Contact us if you are in doubt.
* We make no guarantees that this code is fit for any purpose.
* Visit http://www.pragmaticprogrammer.com/titles/tpantlr2 for more book information.
***/
import org.antlr.v4.runtime.*;
import org.antlr.v4.runtime.tree.*;
import java.io.FileInputStream;
import java.io.InputStream;

class PropertyFilePrinter extends PropertyFileParser {

public PropertyFilePrinter(TokenStream input) {
super(input);
}

@Override
void defineProperty(Token name, Token value) {
System.out.println(name.getText()+"="+value.getText());
}
}

public class PropertyFileRide {
public static void main(String[] args) throws Exception {
String inputFile = null;
if ( args.length>0 ) inputFile = args[0];
InputStream is = System.in;
if ( inputFile!=null ) is = new FileInputStream(inputFile);
ANTLRInputStream input = new ANTLRInputStream(is);
PropertyFileLexer lexer = new PropertyFileLexer(input);
CommonTokenStream tokens = new CommonTokenStream(lexer);
PropertyFilePrinter parser = new PropertyFilePrinter(tokens);
parser.file();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
//: TestPropertyFile.java
/***
* Excerpted from "The Definitive ANTLR 4 Reference",
* published by The Pragmatic Bookshelf.
* Copyrights apply to this code. It may not be used to create training material,
* courses, books, articles, and the like. Contact us if you are in doubt.
* We make no guarantees that this code is fit for any purpose.
* Visit http://www.pragmaticprogrammer.com/titles/tpantlr2 for more book information.
***/
import org.antlr.v4.misc.OrderedHashMap;
import org.antlr.v4.runtime.*;
import org.antlr.v4.runtime.tree.*;

import java.io.*;
import java.util.Map;

public class TestPropertyFile {
public static class PropertyFileLoader extends PropertyFileBaseListener {
Map<String,String> props = new OrderedHashMap<String, String>();
public void exitProp(PropertyFileParser.PropContext ctx) {
String id = ctx.ID().getText(); // prop : ID '=' STRING '\n' ;
String value = ctx.STRING().getText();
props.put(id, value);
}
}

public static void main(String[] args) throws Exception {
String inputFile = null;
if ( args.length>0 ) inputFile = args[0];
InputStream is = System.in;
if ( inputFile!=null ) {
is = new FileInputStream(inputFile);
}
ANTLRInputStream input = new ANTLRInputStream(is);
PropertyFileLexer lexer = new PropertyFileLexer(input);
CommonTokenStream tokens = new CommonTokenStream(lexer);
PropertyFileParser parser = new PropertyFileParser(tokens);
ParseTree tree = parser.file();

// create a standard ANTLR parse tree walker
ParseTreeWalker walker = new ParseTreeWalker();
// create listener then feed to walker
PropertyFileLoader loader = new PropertyFileLoader();
walker.walk(loader, tree); // walk parse tree
System.out.println(loader.props); // print results
}
}
1
2
user="parrt"
machine="maniac"

PropertyFile2

1
2
3
4
5
grammar PropertyFile;
file : prop+ ;
prop : ID '=' STRING '\n' ;
ID : [a-z]+ ;
STRING : '"' .*? '"' ;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
//: TestPropertyFile.java

/**
* @author jumormt
* @version 1.0
*/

import org.antlr.v4.runtime.*;
import org.antlr.v4.runtime.tree.*;

import org.antlr.v4.misc.OrderedHashMap;

import java.io.FileInputStream;
import java.io.InputStream;

import java.util.HashMap;
import java.util.Map;

public class TestPropertyFile{

public static class PropertyFileLoader extends PropertyFileBaseVisitor<Integer>{
Map<String, String> prps = new OrderedHashMap<String, String>();

@Override
public Integer visitProp(PropertyFileParser.PropContext ctx) {
String id = ctx.ID().getText();
String value = ctx.STRING().getText();
prps.put(id, value);
return 0;
}


}
public static void main(String[] args) throws Exception{
String inputFile = null;
if ( args.length>0 ) inputFile = args[0];
InputStream is = System.in;
if ( inputFile!=null ) is = new FileInputStream(inputFile);
PropertyFileLexer lexer = new PropertyFileLexer(new ANTLRInputStream(is));
CommonTokenStream tokens = new CommonTokenStream(lexer);
PropertyFileParser parser = new PropertyFileParser(tokens);
parser.setBuildParseTree(true); // tell ANTLR to build a parse tree
ParseTree tree = parser.file();

PropertyFileLoader loader = new PropertyFileLoader();

loader.visit(tree);
System.out.println(loader.prps);

}
}

chap8

LoadCSV

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
//: LoadCSV.java

/** chap8/8.1
* @author jumormt
* @version 1.0
*/

import org.antlr.v4.runtime.*;
import org.antlr.v4.runtime.tree.*;

import java.io.*;
import java.util.*;

public class LoadCSV{


public static class Loader extends CSVBaseListener{
public static final String EMPTY = "";

List<Map<String, String>> rows = new ArrayList<Map<String, String>>();

List<String> header;

List<String> currentRowFieldVaule;

@Override
public void exitEmpty(CSVParser.EmptyContext ctx) {
currentRowFieldVaule.add(EMPTY);
}

@Override
public void exitString(CSVParser.StringContext ctx) {
currentRowFieldVaule.add(ctx.STRING().getText());
}

@Override
public void exitText(CSVParser.TextContext ctx) {
currentRowFieldVaule.add(ctx.TEXT().getText());
}

@Override
public void exitHdr(CSVParser.HdrContext ctx) {
header = new ArrayList<String>();
header.addAll(currentRowFieldVaule);
}

@Override
public void enterRow(CSVParser.RowContext ctx) {
currentRowFieldVaule = new ArrayList<String>();
}

@Override
public void exitRow(CSVParser.RowContext ctx) {
if (ctx.getParent().getRuleIndex() == CSVParser.RULE_hdr){
return;
}

Map<String, String> mp = new LinkedHashMap<String, String>();

int i = 0;
for (String v : currentRowFieldVaule){
mp.put(header.get(i), v);
++i;
}
rows.add(mp);
}


}
public static void main(String[] args) throws Exception{
String inputFile = null;
if ( args.length>0 ) inputFile = args[0];
InputStream is = System.in;
if ( inputFile!=null ) is = new FileInputStream(inputFile);
ANTLRInputStream input = new ANTLRInputStream(is);
CSVLexer lexer = new CSVLexer(input);
CommonTokenStream tokens = new CommonTokenStream(lexer);
CSVParser paser = new CSVParser(tokens);

ParseTree tree = paser.file();

ParseTreeWalker walker = new ParseTreeWalker();
Loader loader = new Loader();
walker.walk(loader, tree);
System.out.println(loader.rows);
}
}

JSON2XML

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
//:JSON2XML.java

import org.antlr.v4.runtime.*;
import org.antlr.v4.runtime.tree.*;

import java.io.*;
import java.util.*;

/** chap8/8.2
* @author jumormt
* @version 1.0
*/

public class JSON2XML{

public static class XMLEmitter extends JSONBaseListener{

ParseTreeProperty<String> xml = new ParseTreeProperty<String>();
void setXml(ParseTree t, String v) {xml.put(t, v);}
String getXml(ParseTree t){return xml.get(t);}

@Override
public void exitJson(JSONParser.JsonContext ctx) {
setXml(ctx, getXml(ctx.getChild(0)));
}

@Override
public void exitEmptyArray(JSONParser.EmptyArrayContext ctx) {
setXml(ctx, "");
}

@Override
public void exitArrayOfValues(JSONParser.ArrayOfValuesContext ctx) {
StringBuilder buf = new StringBuilder();
buf.append('\n');
for (JSONParser.ValueContext p:ctx.value()){
buf.append("<element>");
buf.append(getXml(p));
buf.append("</element>");
buf.append('\n');
}
setXml(ctx, buf.toString());
}

@Override
public void exitEmptyObject(JSONParser.EmptyObjectContext ctx) {
setXml(ctx, "");
}

@Override
public void exitAnObject(JSONParser.AnObjectContext ctx) {
StringBuilder buf = new StringBuilder();
buf.append('\n');
for (JSONParser.PairContext p:ctx.pair()){
buf.append(getXml(p));
}
setXml(ctx, buf.toString());
}

@Override
public void exitPair(JSONParser.PairContext ctx) {
String tag = stripQuotes(ctx.STRING().getText());
JSONParser.ValueContext cvalue = ctx.value();
String x = String.format("<%s>%s</%s>\n", tag, getXml(cvalue), tag);
setXml(ctx, x);
}

@Override
public void exitString(JSONParser.StringContext ctx) {
setXml(ctx, stripQuotes(ctx.getText()));
}

@Override
public void exitAtom(JSONParser.AtomContext ctx) {
setXml(ctx, ctx.getText());
}

@Override
public void exitObjectValue(JSONParser.ObjectValueContext ctx) {
setXml(ctx, getXml(ctx.object()));
}

@Override
public void exitArrayValue(JSONParser.ArrayValueContext ctx) {
setXml(ctx, getXml(ctx.array()));
}

public static String stripQuotes(String s) {
if ( s==null || s.charAt(0)!='"' ) return s;
return s.substring(1, s.length() - 1);
}
}

public static void main(String[] args) throws Exception{
String inputFile = null;
if ( args.length>0 ) inputFile = args[0];
InputStream is = System.in;
if ( inputFile!=null ) is = new FileInputStream(inputFile);
ANTLRInputStream input = new ANTLRInputStream(is);
JSONLexer lexer = new JSONLexer(input);
CommonTokenStream tokenStream = new CommonTokenStream(lexer);
JSONParser parser = new JSONParser(tokenStream);

ParseTree tree = parser.json();

ParseTreeWalker walker = new ParseTreeWalker();
XMLEmitter xmlEmitter = new XMLEmitter();
walker.walk(xmlEmitter, tree);
System.out.println(xmlEmitter.getXml(tree));
}
}

CALLGRAPH

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
//: CallGraph.java

/** chap8/8.3
* @author jumormt
* @version 1.0
*/

import org.antlr.v4.runtime.*;
import org.antlr.v4.runtime.tree.*;
import org.antlr.v4.runtime.misc.*;

import java.io.*;
import java.util.*;

public class CallGraph {

/** 调用图 */
static class Graph{

Set<String> nodes = new OrderedHashSet<String>();

MultiMap<String, String> edges = new MultiMap<String, String>();

public void edge(String source, String dest){
edges.map(source, dest);
}

public String toDOT() {
StringBuilder buf = new StringBuilder();
buf.append("digraph G {\n");
buf.append(" ranksep=.25;\n");
buf.append(" edge [arrowsize=.5]\n");
buf.append(" node [shape=circle, fontname=\"ArialNarrow\",\n");
buf.append(" fontsize=12, fixedsize=true, height=.45];\n");
buf.append(" ");
for (String node : nodes) { // print all nodes first
buf.append(node);
buf.append("; ");
}
buf.append("\n");
for (String src : edges.keySet()) {
for (String trg : edges.get(src)) {
buf.append(" ");
buf.append(src);
buf.append(" -> ");
buf.append(trg);
buf.append(";\n");
}
}
buf.append("}\n");
return buf.toString();
}
}

public static class FunctionListener extends CymbolBaseListener{

Graph gp = new Graph();
String currentFunctionName = null;

@Override
public void enterFunctionDecl(CymbolParser.FunctionDeclContext ctx) {
currentFunctionName = ctx.ID().getText();
gp.nodes.add(currentFunctionName);
}

@Override
public void exitCall(CymbolParser.CallContext ctx) {
String functionName = ctx.ID().getText();
gp.edge(currentFunctionName, functionName);
}
}

public static void main(String[] args) throws Exception{
String inputFile = null;
if ( args.length>0 ) inputFile = args[0];
InputStream is = System.in;
if ( inputFile!=null ) {
is = new FileInputStream(inputFile);
}
ANTLRInputStream input = new ANTLRInputStream(is);
CymbolLexer lexer = new CymbolLexer(input);
CommonTokenStream tokens = new CommonTokenStream(lexer);
CymbolParser parser = new CymbolParser(tokens);
parser.setBuildParseTree(true);
ParseTree tree = parser.file();
// show tree in text form
// System.out.println(tree.toStringTree(parser));

ParseTreeWalker walker = new ParseTreeWalker();
FunctionListener collector = new FunctionListener();
walker.walk(collector, tree);
System.out.println(collector.gp.toString());
System.out.println(collector.gp.toDOT());

// Here's another example that uses StringTemplate to generate output
// System.out.println(collector.graph.toST().render());
}


}

DRefPhase

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
// Cymbol.g4
/** 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 below becomes the following non-left recursive rule:
expr[int _p]
: ( '-' expr[6]
| '!' expr[5]
| ID
| INT
| '(' expr ')'
)
( {8 >= $_p}? '*' expr[9]
| {7 >= $_p}? ('+'|'-') expr[8]
| {4 >= $_p}? '==' expr[5]
| {10 >= $_p}? '[' expr ']'
| {9 >= $_p}? '(' exprList? ')'
)*
;
*/

expr: ID '(' exprList? ')' # Call
| expr '[' expr ']' # Index
| '-' expr # Negate
| '!' expr # Not
| expr '*' expr # Mult
| expr ('+'|'-') expr # AddSub
| expr '==' expr # Equal
| ID # Var
| INT # Int
| '(' expr ')' # Parens
;

exprList : expr (',' expr)* ; // arg list

K_FLOAT : 'float';
K_INT : 'int';
K_VOID : 'void';
ID : LETTER (LETTER | [0-9])* ;
fragment
LETTER : [a-zA-Z] ;

INT : [0-9]+ ;

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

SL_COMMENT
: '//' .*? '\n' -> skip
;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
//: DefPhase.java

import SymbolList.*;
import org.antlr.v4.runtime.tree.*;
import org.antlr.v4.runtime.*;


public class DefPhase extends CymbolBaseListener{

ParseTreeProperty<Scope> scopes = new ParseTreeProperty<Scope>();
GlobalScope globals;
Scope currentScope;

@Override
public void enterFile(CymbolParser.FileContext ctx) {
globals = new GlobalScope(null);
currentScope = globals;
}

@Override
public void exitFile(CymbolParser.FileContext ctx) {
System.out.println(globals);
}

@Override
public void enterFunctionDecl(CymbolParser.FunctionDeclContext ctx) {
String name = ctx.ID().getText();
int typeTokenType = ctx.type().start.getType();
Symbol.Type type = CheckSymbols.getType(typeTokenType);

// push new scope by making new one that points to enclosing scope
FunctionSymbol function = new FunctionSymbol(name, type, currentScope);
currentScope.define(function); // Define function in current scope
saveScope(ctx, function); // Push: set function's parent to current
currentScope = function; // Current scope is now function scope
}

void saveScope(ParserRuleContext ctx, Scope s) { scopes.put(ctx, s); }

@Override
public void exitFunctionDecl(CymbolParser.FunctionDeclContext ctx) {
System.out.println(currentScope);
currentScope = currentScope.getEnclosingScope();
}

public void enterBlock(CymbolParser.BlockContext ctx) {
// push new local scope
currentScope = new LocalScope(currentScope);
saveScope(ctx, currentScope);
}

public void exitBlock(CymbolParser.BlockContext ctx) {
System.out.println(currentScope);
currentScope = currentScope.getEnclosingScope(); // pop scope
}

public void exitFormalParameter(CymbolParser.FormalParameterContext ctx) {
defineVar(ctx.type(), ctx.ID().getSymbol());
}

public void exitVarDecl(CymbolParser.VarDeclContext ctx) {
defineVar(ctx.type(), ctx.ID().getSymbol());
}

void defineVar(CymbolParser.TypeContext typeCtx, Token nameToken) {
int typeTokenType = typeCtx.start.getType();
Symbol.Type type = CheckSymbols.getType(typeTokenType);
VariableSymbol var = new VariableSymbol(nameToken.getText(), type);
currentScope.define(var); // Define symbol in current scope
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
//: RefPhase.java
/***
* Excerpted from "The Definitive ANTLR 4 Reference",
* published by The Pragmatic Bookshelf.
* Copyrights apply to this code. It may not be used to create training material,
* courses, books, articles, and the like. Contact us if you are in doubt.
* We make no guarantees that this code is fit for any purpose.
* Visit http://www.pragmaticprogrammer.com/titles/tpantlr2 for more book information.
***/
import org.antlr.v4.runtime.tree.ParseTreeProperty;
import SymbolList.*;

public class RefPhase extends CymbolBaseListener {
ParseTreeProperty<Scope> scopes;
GlobalScope globals;
Scope currentScope; // resolve symbols starting in this scope

public RefPhase(GlobalScope globals, ParseTreeProperty<Scope> scopes) {
this.scopes = scopes;
this.globals = globals;
}
public void enterFile(CymbolParser.FileContext ctx) {
currentScope = globals;
}

public void enterFunctionDecl(CymbolParser.FunctionDeclContext ctx) {
currentScope = scopes.get(ctx);
}
public void exitFunctionDecl(CymbolParser.FunctionDeclContext ctx) {
currentScope = currentScope.getEnclosingScope();
}

public void enterBlock(CymbolParser.BlockContext ctx) {
currentScope = scopes.get(ctx);
}
public void exitBlock(CymbolParser.BlockContext ctx) {
currentScope = currentScope.getEnclosingScope();
}

public void exitVar(CymbolParser.VarContext ctx) {
String name = ctx.ID().getSymbol().getText();
Symbol var = currentScope.resolve(name);
if ( var==null ) {
CheckSymbols.error(ctx.ID().getSymbol(), "no such variable: "+name);
}
if ( var instanceof FunctionSymbol ) {
CheckSymbols.error(ctx.ID().getSymbol(), name+" is not a variable");
}
}

public void exitCall(CymbolParser.CallContext ctx) {
// can only handle f(...) not expr(...)
String funcName = ctx.ID().getText();
Symbol meth = currentScope.resolve(funcName);
if ( meth==null ) {
CheckSymbols.error(ctx.ID().getSymbol(), "no such function: "+funcName);
}
if ( meth instanceof VariableSymbol ) {
CheckSymbols.error(ctx.ID().getSymbol(), funcName+" is not a function");
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
//: CheckSymbols.java

/** chap8/8.4 main
* @author jumormt
* @version 1.0
*/

/***
* Excerpted from "The Definitive ANTLR 4 Reference",
* published by The Pragmatic Bookshelf.
* Copyrights apply to this code. It may not be used to create training material,
* courses, books, articles, and the like. Contact us if you are in doubt.
* We make no guarantees that this code is fit for any purpose.
* Visit http://www.pragmaticprogrammer.com/titles/tpantlr2 for more book information.
***/
import org.antlr.v4.runtime.ANTLRInputStream;
import org.antlr.v4.runtime.CommonTokenStream;
import org.antlr.v4.runtime.ParserRuleContext;
import org.antlr.v4.runtime.Token;
import org.antlr.v4.runtime.tree.*;

import java.io.FileInputStream;
import java.io.InputStream;

import SymbolList.*;

public class CheckSymbols {
public static Symbol.Type getType(int tokenType) {
switch ( tokenType ) {
case CymbolParser.K_VOID : return Symbol.Type.tVOID;
case CymbolParser.K_INT : return Symbol.Type.tINT;
case CymbolParser.K_FLOAT : return Symbol.Type.tFLOAT;
}
return Symbol.Type.tINVALID;
}

public static void error(Token t, String msg) {
System.err.printf("line %d:%d %s\n", t.getLine(), t.getCharPositionInLine(),
msg);
}

public void process(String[] args) throws Exception {
String inputFile = null;
if ( args.length>0 ) inputFile = args[0];
InputStream is = System.in;
if ( inputFile!=null ) {
is = new FileInputStream(inputFile);
}
ANTLRInputStream input = new ANTLRInputStream(is);
CymbolLexer lexer = new CymbolLexer(input);
CommonTokenStream tokens = new CommonTokenStream(lexer);
CymbolParser parser = new CymbolParser(tokens);
parser.setBuildParseTree(true);
ParseTree tree = parser.file();
// show tree in text form
// System.out.println(tree.toStringTree(parser));

ParseTreeWalker walker = new ParseTreeWalker();
DefPhase def = new DefPhase();
walker.walk(def, tree);
// create next phase and feed symbol table info from def to ref phase
RefPhase ref = new RefPhase(def.globals, def.scopes);
walker.walk(ref, tree);
}

public static void main(String[] args) throws Exception {
new CheckSymbols().process(args);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
//vars.cymbol
int f(int x, float y) {
g(); // forward reference is ok
i = 3; // no declaration for i (error)
g = 4; // g is not variable (error)
return x + y; // x, y are defined, so no problem
}

void g() {
int x = 0;
float y;
y = 9; // y is defined
f(); // backward reference is ok
z(); // no such function (error)
y(); // y is not function (error)
x = f; // f is not a variable (error)
}

//vars2.cymbol
int x; //(1)
int y;
void a() //(2)
{ //(3)
int x;
x = 1; // x resolves to current scope, not x in global scope
y = 2; // y is not found in current scope, but resolves in global
{ int y = x; } //(4)
}
void b(int z) //(5)
{ } //(6)

SymbolList

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
//: Scope.java
package SymbolList;

/***
* Excerpted from "The Definitive ANTLR 4 Reference",
* published by The Pragmatic Bookshelf.
* Copyrights apply to this code. It may not be used to create training material,
* courses, books, articles, and the like. Contact us if you are in doubt.
* We make no guarantees that this code is fit for any purpose.
* Visit http://www.pragmaticprogrammer.com/titles/tpantlr2 for more book information.
***/
public interface Scope {
public String getScopeName();

/** Where to look next for symbols */
public Scope getEnclosingScope();

/** Define a symbol in the current scope */
public void define(Symbol sym);

/** Look up name in this scope or in enclosing scope if not here */
public Symbol resolve(String name);
}

//: BaseScope.java
package SymbolList;

import java.util.LinkedHashMap;
import java.util.Map;

public abstract class BaseScope implements Scope {
Scope enclosingScope; // null if global (outermost) scope
Map<String, Symbol> symbols = new LinkedHashMap<String, Symbol>();

public BaseScope(Scope enclosingScope) { this.enclosingScope = enclosingScope; }

public Symbol resolve(String name) {
Symbol s = symbols.get(name);
if ( s!=null ) return s;
// if not here, check any enclosing scope
if ( enclosingScope != null ) return enclosingScope.resolve(name);
return null; // not found
}

public void define(Symbol sym) {
symbols.put(sym.name, sym);
sym.scope = this; // track the scope in each symbol
}

public Scope getEnclosingScope() { return enclosingScope; }

public String toString() { return getScopeName()+":"+symbols.keySet().toString(); }
}

//: SymbolList/GlobalScope.java
package SymbolList;

/***
* Excerpted from "The Definitive ANTLR 4 Reference",
* published by The Pragmatic Bookshelf.
* Copyrights apply to this code. It may not be used to create training material,
* courses, books, articles, and the like. Contact us if you are in doubt.
* We make no guarantees that this code is fit for any purpose.
* Visit http://www.pragmaticprogrammer.com/titles/tpantlr2 for more book information.
***/
public class GlobalScope extends BaseScope {
public GlobalScope(Scope enclosingScope) { super(enclosingScope); }
public String getScopeName() { return "globals"; }
}

//: SymbolList/LocalScope.java
package SymbolList;

/***
* Excerpted from "The Definitive ANTLR 4 Reference",
* published by The Pragmatic Bookshelf.
* Copyrights apply to this code. It may not be used to create training material,
* courses, books, articles, and the like. Contact us if you are in doubt.
* We make no guarantees that this code is fit for any purpose.
* Visit http://www.pragmaticprogrammer.com/titles/tpantlr2 for more book information.
***/
public class LocalScope extends BaseScope {
public LocalScope(Scope parent) { super(parent); }
public String getScopeName() { return "locals"; }
}

//: Symbol.java
package SymbolList;

/***
* Excerpted from "The Definitive ANTLR 4 Reference",
* published by The Pragmatic Bookshelf.
* Copyrights apply to this code. It may not be used to create training material,
* courses, books, articles, and the like. Contact us if you are in doubt.
* We make no guarantees that this code is fit for any purpose.
* Visit http://www.pragmaticprogrammer.com/titles/tpantlr2 for more book information.
***/
public class Symbol { // A generic programming language symbol
public static enum Type {tINVALID, tVOID, tINT, tFLOAT}

String name; // All symbols at least have a name
Type type;
Scope scope; // All symbols know what scope contains them.

public Symbol(String name) { this.name = name; }
public Symbol(String name, Type type) { this(name); this.type = type; }
public String getName() { return name; }

public String toString() {
if ( type!=Type.tINVALID ) return '<'+getName()+":"+type+'>';
return getName();
}
}

//: FunctionSymbol.java
package SymbolList;

/***
* Excerpted from "The Definitive ANTLR 4 Reference",
* published by The Pragmatic Bookshelf.
* Copyrights apply to this code. It may not be used to create training material,
* courses, books, articles, and the like. Contact us if you are in doubt.
* We make no guarantees that this code is fit for any purpose.
* Visit http://www.pragmaticprogrammer.com/titles/tpantlr2 for more book information.
***/
import java.util.LinkedHashMap;
import java.util.Map;

public class FunctionSymbol extends Symbol implements Scope {
Map<String, Symbol> arguments = new LinkedHashMap<String, Symbol>();
Scope enclosingScope;

public FunctionSymbol(String name, Type retType, Scope enclosingScope) {
super(name, retType);
this.enclosingScope = enclosingScope;
}

public Symbol resolve(String name) {
Symbol s = arguments.get(name);
if ( s!=null ) return s;
// if not here, check any enclosing scope
if ( getEnclosingScope() != null ) {
return getEnclosingScope().resolve(name);
}
return null; // not found
}

public void define(Symbol sym) {
arguments.put(sym.name, sym);
sym.scope = this; // track the scope in each symbol
}

public Scope getEnclosingScope() { return enclosingScope; }
public String getScopeName() { return name; }

public String toString() { return "function"+super.toString()+":"+arguments.values(); }
}

//: VariableSymbol.java
package SymbolList;

/***
* Excerpted from "The Definitive ANTLR 4 Reference",
* published by The Pragmatic Bookshelf.
* Copyrights apply to this code. It may not be used to create training material,
* courses, books, articles, and the like. Contact us if you are in doubt.
* We make no guarantees that this code is fit for any purpose.
* Visit http://www.pragmaticprogrammer.com/titles/tpantlr2 for more book information.
***/
/** Represents a variable definition (name,type) in symbol table */
public class VariableSymbol extends Symbol {
public VariableSymbol(String name, Type type) { super(name, type); }
}
-------------本文结束感谢您的阅读-------------

本文标题:Antlr4使用教程

文章作者:ChengXiao

发布时间:2018年09月04日 - 13:09

最后更新:2018年09月04日 - 17:09

原始链接:http://chengxiao19961022.github.io/2018/09/04/Antlr4使用教程/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。

你的鼓励是我前进的动力~