片段在ANTLR中意味着什么?
我见过这两条规则:
fragment DIGIT : '0'..'9';
Run Code Online (Sandbox Code Playgroud)
和
DIGIT : '0'..'9';
Run Code Online (Sandbox Code Playgroud)
有什么不同?
sir*_*nce 99
片段有点类似于内联函数:它使语法更易读,更易于维护.
片段永远不会被视为令牌,它只用于简化语法.
考虑:
NUMBER: DIGITS | OCTAL_DIGITS | HEX_DIGITS;
fragment DIGITS: '1'..'9' '0'..'9'*;
fragment OCTAL_DIGITS: '0' '0'..'7'+;
fragment HEX_DIGITS: '0x' ('0'..'9' | 'a'..'f' | 'A'..'F')+;
Run Code Online (Sandbox Code Playgroud)
在此示例中,匹配NUMBER将始终向词法分析器返回NUMBER,无论它是否匹配"1234","0xab12"或"0777".
Nas*_*imi 16
根据Deflitive Antlr4参考书:
前缀为fragment的规则只能从其他词法分析器规则中调用; 他们本身并不是代币.
实际上它们会提高语法的可读性.
看看这个例子:
STRING : '"' (ESC | ~["\\])* '"' ;
fragment ESC : '\\' (["\\/bfnrt] | UNICODE) ;
fragment UNICODE : 'u' HEX HEX HEX HEX ;
fragment HEX : [0-9a-fA-F] ;
Run Code Online (Sandbox Code Playgroud)
STRING是使用片段规则(如ESC)的词法分析器.在Esc规则中使用Unicode,在Unicode片段规则中使用Hex.ESC和UNICODE和HEX规则不能明确使用.
这篇博文有一个非常明显的例子,fragment它有很大的不同:
grammar number;
number: INT;
DIGIT : '0'..'9';
INT : DIGIT+;
Run Code Online (Sandbox Code Playgroud)
语法将识别'42'但不识别'7'.您可以通过将数字设为片段(或在INT之后移动DIGIT)来修复它.
权威的ANTLR 4参考(页106)?
带片段前缀的规则只能从其他词法分析器规则中调用;它们本身并不是代币。
情况1 :(如果需要RULE1,RULE2,RULE3 实体或组信息)
rule0 : RULE1 | RULE2 | RULE3 ;
RULE1 : [A-C]+ ;
RULE2 : [DEF]+ ;
RULE3 : ('G'|'H'|'I')+ ;
Run Code Online (Sandbox Code Playgroud)
案例2 :(如果我不在乎RULE1,RULE2,RULE3,我只关注RULE0)
RULE0 : [A-C]+ | [DEF]+ | ('G'|'H'|'I')+ ;
// RULE0 is a terminal node.
// You can't name it 'rule0', or you will get syntax errors:
// 'A-C' came as a complete surprise to me while matching alternative
// 'DEF' came as a complete surprise to me while matching alternative
Run Code Online (Sandbox Code Playgroud)
Case3 :(与Case2等效,因此比Case2更具可读性)
RULE0 : RULE1 | RULE2 | RULE3 ;
fragment RULE1 : [A-C]+ ;
fragment RULE2 : [DEF]+ ;
fragment RULE3 : ('G'|'H'|'I')+ ;
// You can't name it 'rule0', or you will get warnings:
// warning(125): implicit definition of token RULE1 in parser
// warning(125): implicit definition of token RULE2 in parser
// warning(125): implicit definition of token RULE3 in parser
// and failed to capture rule0 content (?)
Run Code Online (Sandbox Code Playgroud)

目标:识别[ABC]+,[DEF]+,[GHI]+令牌
input.txt
ABBCCCDDDDEEEEE ABCDE
FFGGHHIIJJKK FGHIJK
ABCDEFGHIJKL
Run Code Online (Sandbox Code Playgroud)
主程序
ABBCCCDDDDEEEEE ABCDE
FFGGHHIIJJKK FGHIJK
ABCDEFGHIJKL
Run Code Online (Sandbox Code Playgroud)
Alphabet.g4(案例1)
import sys
from antlr4 import *
from AlphabetLexer import AlphabetLexer
from AlphabetParser import AlphabetParser
from AlphabetListener import AlphabetListener
class MyListener(AlphabetListener):
# Exit a parse tree produced by AlphabetParser#content.
def exitContent(self, ctx:AlphabetParser.ContentContext):
pass
# (For Case1 Only) enable it when testing Case1
# Exit a parse tree produced by AlphabetParser#rule0.
def exitRule0(self, ctx:AlphabetParser.Rule0Context):
print(ctx.getText())
# end-of-class
def main():
file_name = sys.argv[1]
input = FileStream(file_name)
lexer = AlphabetLexer(input)
stream = CommonTokenStream(lexer)
parser = AlphabetParser(stream)
tree = parser.content()
print(tree.toStringTree(recog=parser))
listener = MyListener()
walker = ParseTreeWalker()
walker.walk(listener, tree)
# end-of-def
main()
Run Code Online (Sandbox Code Playgroud)
结果:
grammar Alphabet;
content : (rule0|ANYCHAR)* EOF;
rule0 : RULE1 | RULE2 | RULE3 ;
RULE1 : [A-C]+ ;
RULE2 : [DEF]+ ;
RULE3 : ('G'|'H'|'I')+ ;
ANYCHAR : . -> skip;
Run Code Online (Sandbox Code Playgroud)
Alphabet.g4(案例2)
# Input data (for reference)
# ABBCCCDDDDEEEEE ABCDE
# FFGGHHIIJJKK FGHIJK
# ABCDEFGHIJKL
$ python3 Main.py input.txt
(content (rule0 ABBCCC) (rule0 DDDDEEEEE) (rule0 ABC) (rule0 DE) (rule0 FF) (rule0 GGHHII) (rule0 F) (rule0 GHI) (rule0 ABC) (rule0 DEF) (rule0 GHI) <EOF>)
ABBCCC
DDDDEEEEE
ABC
DE
FF
GGHHII
F
GHI
ABC
DEF
GHI
Run Code Online (Sandbox Code Playgroud)
Alphabet.g4(案例3)
grammar Alphabet;
content : (RULE0|ANYCHAR)* EOF;
RULE0 : [A-C]+ | [DEF]+ | ('G'|'H'|'I')+ ;
ANYCHAR : . -> skip;
Run Code Online (Sandbox Code Playgroud)
结果:
grammar Alphabet;
content : (RULE0|ANYCHAR)* EOF;
RULE0 : RULE1 | RULE2 | RULE3 ;
fragment RULE1 : [A-C]+ ;
fragment RULE2 : [DEF]+ ;
fragment RULE3 : ('G'|'H'|'I')+ ;
ANYCHAR : . -> skip;
Run Code Online (Sandbox Code Playgroud)
您看到“捕获组”和“非捕获组”部分吗?
目标:识别八进制/十进制/十六进制数字
input.txt
0
123
1~9999
001~077
0xFF, 0x01, 0xabc123
Run Code Online (Sandbox Code Playgroud)
g.4
# Input data (for reference)
# ABBCCCDDDDEEEEE ABCDE
# FFGGHHIIJJKK FGHIJK
# ABCDEFGHIJKL
$ python3 Main.py input.txt
(content ABBCCC DDDDEEEEE ABC DE FF GGHHII F GHI ABC DEF GHI <EOF>)
Run Code Online (Sandbox Code Playgroud)
主程序
0
123
1~9999
001~077
0xFF, 0x01, 0xabc123
Run Code Online (Sandbox Code Playgroud)
结果:
grammar Number;
content
: (number|ANY_CHAR)* EOF
;
number
: DECIMAL_NUMBER
| OCTAL_NUMBER
| HEXADECIMAL_NUMBER
;
DECIMAL_NUMBER
: [1-9][0-9]*
| '0'
;
OCTAL_NUMBER
: '0' '0'..'9'+
;
HEXADECIMAL_NUMBER
: '0x'[0-9A-Fa-f]+
;
ANY_CHAR
: .
;
Run Code Online (Sandbox Code Playgroud)
如果添加了改良剂“片段”来DECIMAL_NUMBER,OCTAL_NUMBER,HEXADECIMAL_NUMBER,你将无法捕捉到一些实体(因为它们不是记号了)。结果将是:
import sys
from antlr4 import *
from NumberLexer import NumberLexer
from NumberParser import NumberParser
from NumberListener import NumberListener
class Listener(NumberListener):
# Exit a parse tree produced by NumberParser#Number.
def exitNumber(self, ctx:NumberParser.NumberContext):
print('%8s, dec: %-8s, oct: %-8s, hex: %-8s' % (ctx.getText(),
ctx.DECIMAL_NUMBER(), ctx.OCTAL_NUMBER(), ctx.HEXADECIMAL_NUMBER()))
# end-of-def
# end-of-class
def main():
input = FileStream(sys.argv[1])
lexer = NumberLexer(input)
stream = CommonTokenStream(lexer)
parser = NumberParser(stream)
tree = parser.content()
print(tree.toStringTree(recog=parser))
listener = Listener()
walker = ParseTreeWalker()
walker.walk(listener, tree)
# end-of-def
main()
Run Code Online (Sandbox Code Playgroud)