4 ответов:
фрагмент несколько сродни встроенной функции: это делает грамматику более читаемой и простой в обслуживании.
фрагмент никогда не будет считаться маркером, он служит только для упрощения грамматики.
считаем:
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')+;в этом примере сопоставление числа всегда возвращает число лексеру, независимо от того, соответствует ли оно "1234", "0xab12" или "0777".
согласно окончательному справочнику Antlr4:
правила с префиксом fragment могут быть вызваны только из других правил lexer; они не являются токенами сами по себе.
на самом деле они улучшат читаемость ваших грамматик.
рассмотрим пример :
STRING : '"' (ESC | ~["\])* '"' ; fragment ESC : '\' (["\/bfnrt] | UNICODE) ; fragment UNICODE : 'u' HEX HEX HEX HEX ; fragment HEX : [0-9a-fA-F] ;строка-это лексер, использующий правило фрагмента, такое как ESC .Юникод используется в правиле ESC и наговор используется в правиле фрагмент Юникод. ESC и Юникод и шестнадцатеричные правила не могут использоваться явно.
этой блоге имеет очень четкий пример, где
fragmentимеет существенное значение:grammar number; number: INT; DIGIT : '0'..'9'; INT : DIGIT+;грамматика распознает '42', но не '7'. Вы можете исправить это, сделав цифру фрагментом (или перемещая цифру после INT).
окончательная ссылка ANTLR 4 (Страница 106):
правила с префиксом фрагмент может вызываться только из других правил лексера; они не являются токенами сами по себе.
Абстрактные Понятия:
Вопрос 1: ( если мне нужно Правило 1, Правило 2, Правило 3 объекты или информация о группе)
rule0 : RULE1 | RULE2 | RULE3 ; RULE1 : [A-C]+ ; RULE2 : [DEF]+ ; RULE3 : ('G'|'H'|'I')+ ;
Вопрос 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
Вопрос 3: (эквивалентно 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 (?)
различия между Case1 и Case2 / 3 ?
- правила лексера эквивалентны
- каждый из RULE1 / 2 / 3 в Case1 является группой захвата, аналогичной регулярному выражению: (X)
- каждый из RULE1 / 2 / 3 в Case3 является группой без захвата, подобно регулярному выражению: (?:Икс)
![]()
давайте рассмотрим конкретный пример.
цель: определить
[ABC]+,[DEF]+,[GHI]+маркерывход.txt
ABBCCCDDDDEEEEE ABCDE FFGGHHIIJJKK FGHIJK ABCDEFGHIJKL
Main.pyimport 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()
Case1 и результаты:
алфавит.g4 (Case1)
grammar Alphabet; content : (rule0|ANYCHAR)* EOF; rule0 : RULE1 | RULE2 | RULE3 ; RULE1 : [A-C]+ ; RULE2 : [DEF]+ ; RULE3 : ('G'|'H'|'I')+ ; ANYCHAR : . -> skip;результат:
# 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
Case2 / 3 и результаты:
алфавит.Г4 (Вариант 2)
grammar Alphabet; content : (RULE0|ANYCHAR)* EOF; RULE0 : [A-C]+ | [DEF]+ | ('G'|'H'|'I')+ ; ANYCHAR : . -> skip;алфавит.Г4 (Случай 3)
grammar Alphabet; content : (RULE0|ANYCHAR)* EOF; RULE0 : RULE1 | RULE2 | RULE3 ; fragment RULE1 : [A-C]+ ; fragment RULE2 : [DEF]+ ; fragment RULE3 : ('G'|'H'|'I')+ ; ANYCHAR : . -> skip;результат:
# 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>)ты видел "группы захвата" и "группы без захвата" части?
давайте посмотрим конкретный пример 2.
цель: определить восьмеричные / десятичные / шестнадцатеричные числа
вход.txt
0 123 1~9999 001~077 0xFF, 0x01, 0xabc123
количество.g4
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 : . ;
Main.py
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()
результат:
# Input data (for reference) # 0 # 123 # 1~9999 # 001~077 # 0xFF, 0x01, 0xabc123 $ python3 Main.py input.txt (content (number 0) \n (number 123) \n (number 1) ~ (number 9999) \n (number 001) ~ (number 077) \n (number 0xFF) , (number 0x01) , (number 0xabc123) \n <EOF>) 0, dec: 0 , oct: None , hex: None 123, dec: 123 , oct: None , hex: None 1, dec: 1 , oct: None , hex: None 9999, dec: 9999 , oct: None , hex: None 001, dec: None , oct: 001 , hex: None 077, dec: None , oct: 077 , hex: None 0xFF, dec: None , oct: None , hex: 0xFF 0x01, dec: None , oct: None , hex: 0x01 0xabc123, dec: None , oct: None , hex: 0xabc123если вы добавляете модификатор 'фрагмент' в
DECIMAL_NUMBER,OCTAL_NUMBER,HEXADECIMAL_NUMBER, вы не сможете захватить ряд лиц (так как они больше не жетоны). И результат будет:$ python3 Main.py input.txt (content 0 \n 1 2 3 \n 1 ~ 9 9 9 9 \n 0 0 1 ~ 0 7 7 \n 0 x F F , 0 x 0 1 , 0 x a b c 1 2 3 \n <EOF>)

Comments