Apa arti fragmen di ANTLR?
Saya telah melihat kedua aturan tersebut:
fragment DIGIT : '0'..'9';
dan
DIGIT : '0'..'9';
Apa bedanya?
Apa arti fragmen di ANTLR?
Saya telah melihat kedua aturan tersebut:
fragment DIGIT : '0'..'9';
dan
DIGIT : '0'..'9';
Apa bedanya?
Jawaban:
Fragmen agak mirip dengan fungsi sebaris: Fragmen membuat tata bahasa lebih mudah dibaca dan dikelola.
Fragmen tidak akan pernah dihitung sebagai token, ini hanya berfungsi untuk menyederhanakan tata bahasa.
Mempertimbangkan:
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')+;
Dalam contoh ini, mencocokkan NOMOR akan selalu mengembalikan NOMOR ke lexer, terlepas dari apakah itu cocok dengan "1234", "0xab12", atau "0777".
Menurut buku referensi Definitive Antlr4:
Aturan yang diawali dengan fragmen hanya bisa dipanggil dari aturan lexer lain; mereka bukanlah token dengan sendirinya.
sebenarnya mereka akan meningkatkan keterbacaan tata bahasa Anda.
lihat contoh ini:
STRING : '"' (ESC | ~["\\])* '"' ;
fragment ESC : '\\' (["\\/bfnrt] | UNICODE) ;
fragment UNICODE : 'u' HEX HEX HEX HEX ;
fragment HEX : [0-9a-fA-F] ;
STRING adalah lexer yang menggunakan aturan fragmen seperti ESC. Unicode digunakan dalam aturan Esc dan Hex digunakan dalam aturan fragmen Unicode. Aturan ESC dan UNICODE dan HEX tidak dapat digunakan secara eksplisit.
Referensi Definitif ANTLR 4 (Halaman 106) :
Aturan yang diawali dengan fragmen hanya bisa dipanggil dari aturan lexer lain; mereka bukanlah token dengan sendirinya.
Kasus1: (jika saya membutuhkan RULE1, RULE2, entitas RULE3 atau info grup)
rule0 : RULE1 | RULE2 | RULE3 ;
RULE1 : [A-C]+ ;
RULE2 : [DEF]+ ;
RULE3 : ('G'|'H'|'I')+ ;
Case2: (jika saya tidak peduli RULE1, RULE2, RULE3, saya hanya fokus pada 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
Case3: (setara dengan Case2, membuatnya lebih mudah dibaca daripada 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 (?)
Tujuan: mengidentifikasi [ABC]+
, [DEF]+
, [GHI]+
token
input.txt
ABBCCCDDDDEEEEE ABCDE
FFGGHHIIJJKK FGHIJK
ABCDEFGHIJKL
Main.py
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()
Alphabet.g4 (Kasus1)
grammar Alphabet;
content : (rule0|ANYCHAR)* EOF;
rule0 : RULE1 | RULE2 | RULE3 ;
RULE1 : [A-C]+ ;
RULE2 : [DEF]+ ;
RULE3 : ('G'|'H'|'I')+ ;
ANYCHAR : . -> skip;
Hasil:
# 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
Alphabet.g4 (Case2)
grammar Alphabet;
content : (RULE0|ANYCHAR)* EOF;
RULE0 : [A-C]+ | [DEF]+ | ('G'|'H'|'I')+ ;
ANYCHAR : . -> skip;
Alphabet.g4 (Case3)
grammar Alphabet;
content : (RULE0|ANYCHAR)* EOF;
RULE0 : RULE1 | RULE2 | RULE3 ;
fragment RULE1 : [A-C]+ ;
fragment RULE2 : [DEF]+ ;
fragment RULE3 : ('G'|'H'|'I')+ ;
ANYCHAR : . -> skip;
Hasil:
# 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>)
Apakah Anda melihat bagian "grup penangkap" dan "grup bukan penangkap" ?
Sasaran: mengidentifikasi bilangan oktal / desimal / heksadesimal
input.txt
0
123
1~9999
001~077
0xFF, 0x01, 0xabc123
Nomor.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()
Hasil:
# 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
Jika Anda menambahkan modifier 'fragmen' untuk DECIMAL_NUMBER
, OCTAL_NUMBER
, HEXADECIMAL_NUMBER
, Anda tidak akan dapat menangkap entitas jumlah (karena mereka tidak token lagi). Dan hasilnya adalah:
$ 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>)
Posting blog ini memiliki contoh yang sangat jelas yang fragment
membuat perbedaan yang signifikan:
grammar number;
number: INT;
DIGIT : '0'..'9';
INT : DIGIT+;
Tata bahasanya akan mengenali '42' tapi bukan '7'. Anda dapat memperbaikinya dengan membuat digit menjadi fragmen (atau memindahkan DIGIT setelah INT).
fragment
, tetapi urutan aturan lexer.
DIGIT
sebagai fragmen dari INT
memecahkan masalah hanya karena fragmen tidak mendefinisikan token, sehingga membuat INT
aturan leksikal pertama. Saya setuju dengan Anda bahwa ini adalah contoh yang berarti tetapi (imo) hanya untuk yang sudah tahu apa arti fragment
kata kunci itu. Saya merasa agak menyesatkan bagi seseorang yang mencoba mencari tahu penggunaan fragmen yang benar untuk pertama kalinya.
fragment
artinya di ANTLR. Tetapi contoh yang Anda berikan salah: Anda tidak ingin lexer menghasilkanNUMBER
token yang dapat berupa bilangan heksadesimal, desimal, atau oktal. Itu berarti Anda perlu memeriksaNUMBER
token dalam produksi (aturan parser). Anda bisa lebih baik membiarkan lexer hasilINT
,OCT
danHEX
token dan membuat aturan produksi:number : INT | OCT | HEX;
. Dalam contoh seperti itu, aDIGIT
bisa menjadi fragmen yang akan digunakan oleh tokenINT
danHEX
.