Catatan
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba masuk atau mengubah direktori.
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba mengubah direktori.
6.1 Program
Program C# terdiri dari satu atau beberapa file sumber, yang dikenal secara resmi sebagai unit kompilasi (§14.2). Meskipun unit kompilasi mungkin memiliki korespondensi satu-ke-satu dengan file dalam sistem file, korespondensi tersebut tidak diperlukan.
Secara konseptual, program dikompilasi menggunakan tiga langkah:
- Transformasi, yang mengonversi file dari skema repertoar dan pengodean karakter tertentu menjadi urutan karakter Unicode.
- Analisis leksikal, yang menerjemahkan aliran karakter input Unicode ke dalam aliran token.
- Analisis sintaksis, yang menerjemahkan rangkaian token ke dalam kode yang dapat dieksekusi.
Implementasi yang sesuai harus menerima unit kompilasi Unicode yang dikodekan dengan formulir pengodean UTF-8 (sebagaimana didefinisikan oleh standar Unicode), dan mengubahnya menjadi urutan karakter Unicode. Implementasi dapat memilih untuk menerima dan mengubah skema pengodean karakter tambahan (seperti pemetaan karakter UTF-16, UTF-32, atau non-Unicode).
Catatan: Penanganan karakter Unicode NULL (U+0000) ditentukan implementasi. Sangat disarankan agar pengembang menghindari penggunaan karakter ini dalam kode sumber mereka, demi portabilitas dan keterbacaan. Ketika karakter diperlukan dalam karakter atau string harfiah, urutan escape
\0
atau\u0000
dapat digunakan sebagai gantinya. catatan akhir
Catatan: Berada di luar cakupan spesifikasi ini untuk menentukan bagaimana file menggunakan representasi karakter selain Unicode mungkin diubah menjadi urutan karakter Unicode. Namun, selama transformasi seperti itu, disarankan agar karakter pemisah garis biasa (atau urutan) dalam kumpulan karakter lain diterjemahkan ke urutan dua karakter yang terdiri dari karakter carriage-return Unicode (U+000D) diikuti oleh karakter line-feed Unicode (U+000A). Untuk sebagian besar transformasi ini tidak akan memiliki efek yang terlihat; namun, itu akan mempengaruhi interpretasi token harfiah string verbatim (§6.4.5.6). Tujuan dari rekomendasi ini adalah untuk memungkinkan string verbatim literal untuk menghasilkan urutan karakter yang sama ketika unit kompilasinya dipindahkan antara sistem yang mendukung set karakter non-Unicode yang berbeda, khususnya, yang menggunakan urutan karakter yang berbeda untuk pemisahan baris. catatan akhir
6.2 Tata Bahasa
6.2.1 Umum
Spesifikasi ini menyajikan sintaks bahasa pemrograman C# menggunakan dua tata bahasa. Tata bahasa leksikal (§6.2.3) mendefinisikan bagaimana karakter Unicode digabungkan untuk membentuk terminator garis, spasi putih, komentar, token, dan arahan pra-pemrosesan. Tata bahasa sintetis (§6.2.4) mendefinisikan bagaimana token yang dihasilkan dari tata bahasa leksikal digabungkan untuk membentuk program C#.
Semua karakter terminal harus dipahami sebagai karakter Unicode yang sesuai dari rentang U+0020 hingga U+007F, dibandingkan dengan karakter yang terlihat serupa dari rentang karakter Unicode lainnya.
6.2.2 Notasi tata bahasa
Tata bahasa leksikal dan sinactik disajikan dalam bentuk Extended Backus-Naur alat tata bahasa ANTLR.
Meskipun notasi ANTLR digunakan, spesifikasi ini tidak menyajikan "tata bahasa referensi yang siap untuk ANTLR" secara lengkap untuk C#; penulisan lexer dan parser, baik secara manual maupun menggunakan alat seperti ANTLR, berada di luar cakupan spesifikasi bahasa. Dengan kualifikasi tersebut, spesifikasi ini mencoba meminimalkan kesenjangan antara tata bahasa yang ditentukan dan yang diperlukan untuk membangun lexer dan parser di ANTLR.
ANTLR membedakan antara leksikal dan sintaksis, yang disebut parser oleh ANTLR, dalam tata bahasanya dengan memulai aturan leksikal dengan huruf besar dan aturan parser dengan huruf kecil.
Catatan: Tata bahasa leksikal C# (§6.2.3) dan tata bahasa syntactic (§6.2.4) tidak sama persis dengan pembagian ANTLR ke dalam grammer leksikal dan parser. Ketidakcocokan kecil ini berarti bahwa beberapa aturan pengurai ANTLR digunakan saat menentukan tata bahasa leksikal C#. catatan akhir
6.2.3 Tata bahasa leksikal
Tata bahasa leksikal C# disajikan dalam §6,3, §6,4, dan §6,5. Simbol terminal tata bahasa leksikal adalah karakter dari set karakter Unicode, dan tata bahasa leksikal menentukan bagaimana karakter digabungkan untuk membentuk token (§6,4), spasi kosong (§6.3.4), komentar (§6.3.3), dan arahan pra-pemrosesan (§6,5).
Banyak simbol terminal tata bahasa sintik tidak didefinisikan secara eksplisit sebagai token dalam tata bahasa leksikal. Sebaliknya, keuntungan diambil dari perilaku ANTLR bahwa string harfiah dalam tata bahasa diekstraksi sebagai token leksikal implisit; ini memungkinkan kata kunci, operator, dll. diwakili dalam tata bahasa dengan representasi harfiahnya daripada nama token.
Setiap unit kompilasi dalam program C# harus sesuai dengan produksi input tata bahasa leksikal (§6.3.1).
6.2.4 Tata Bahasa Sintaksis
Tata bahasa sinaks C# disajikan dalam klausul, subklasul, dan lampiran yang mengikuti subklasul ini. Simbol terminal tata bahasa sintetis adalah token yang ditentukan secara eksplisit oleh tata bahasa leksikal dan secara implisit oleh string harfiah dalam tata bahasa itu sendiri (§6.2.3). Tata bahasa syntactic menentukan bagaimana token digabungkan untuk membentuk program C#.
Setiap unit kompilasi dalam program C# harus sesuai dengan produksi compilation_unit (§14,2) dari tata bahasa sinaktik.
6.2.5 Ambiguitas tata bahasa
Produksi untuk:
- simple_name (§12.8.4),
- member_access (§12.8.7),
- akses_anggota_kondisional_null (§12.8.8),
- dependent_access (§12.8.8),
- base_access (§12.8.15) dan
- pointer_member_access (§23.6.3);
("produksi yang telah dijelaskan") dapat menyebabkan ambiguitas dalam tata bahasa untuk ekspresi.
Produksi ini terjadi dalam konteks di mana nilai dapat terjadi dalam ekspresi, dan memiliki satu atau beberapa alternatif yang diakhir dengan tata bahasa "identifier type_argument_list?
". Ini adalah type_argument_list opsional yang menghasilkan kemungkinan ambiguitas.
Contoh: Pernyataan:
F(G<A, B>(7));
dapat ditafsirkan sebagai panggilan ke
F
dengan dua argumen,G < A
danB > (7)
. Atau, ini dapat ditafsirkan sebagai panggilan keF
dengan satu argumen, yang merupakan panggilan ke metodeG
generik dengan dua argumen jenis dan satu argumen reguler.contoh akhir
Jika urutan token dapat diurai, dalam konteks, sebagai salah satu produksi yang didisambiguasi termasuk type_argument_list opsional (§8.4.2), maka token yang segera mengikuti token penutup >
harus diperiksa dan jika:
- salah satu dari
( ) ] } : ; , . ? == != | ^ && || & [
; atau - salah satu operator
< <= >= is as
relasional; atau - istilah kueri kontekstual yang muncul di dalam ekspresi kueri.
kemudian type_argument_list harus dipertahankan sebagai komponen dari produksi yang sudah ditentukan dan kemungkinan penguraian lain dari urutan token harus dibuang. Jika tidak, token yang diurai sebagai type_argument_list tidak akan dianggap sebagai bagian dari produksi yang tidak ambigu, bahkan jika tidak ada penguraian lain yang mungkin dari token tersebut.
Catatan: Aturan disambiguasi ini tidak akan diterapkan saat mengurai produksi lain bahkan jika sama-sama berakhiran "
identifier type_argument_list?
"; produksi tersebut akan diurai seperti biasa. Contohnya meliputi: namespace_or_type_name (§7,8); named_entity (§12.8.23); null_conditional_projection_initializer (§12.8.8); dan qualified_alias_member (§14.8.1). catatan akhir
Contoh: Pernyataan:
F(G<A, B>(7));
akan, menurut aturan ini, ditafsirkan sebagai panggilan ke
F
dengan satu argumen, yang merupakan panggilan ke metodeG
generik dengan dua jenis argumen dan satu argumen reguler. PernyataanF(G<A, B>7); F(G<A, B>>7);
masing-masing akan ditafsirkan sebagai panggilan ke
F
dengan dua argumen. Pernyataanx = F<A> + y;
akan ditafsirkan sebagai operator yang kurang dari, operator yang lebih besar dari dan operator unary-plus, seolah-olah pernyataan telah ditulis
x = (F < A) > (+y)
, alih-alih sebagai simple_name dengan type_argument_list diikuti oleh operator biner-plus. Dalam pernyataanx = y is C<T> && z;
token
C<T>
ditafsirkan sebagai namespace_or_type_name dengan type_argument_list karena adanya token&&
disambiguasi setelah type_argument_list.Ekspresi
(A < B, C > D)
adalah tuple dengan dua elemen yang masing-masing merupakan perbandingan.Ekspresi
(A<B,C> D, E)
adalah tuple dengan dua elemen, yang pertama adalah ekspresi deklarasi.Pemanggilan
M(A < B, C > D, E)
memiliki tiga argumen.Pemanggilan
M(out A<B,C> D, E)
memiliki dua argumen, yang pertama adalah sebuah deklarasiout
.Ekspresi
e is A<B> C
menggunakan pola deklarasi.Label
case A<B> C:
pada kasus menggunakan pola deklarasi.contoh akhir
Saat mengenali relational_expression (§12.12.1), jika alternatif "relational_expressionis
jenis" dan "relational_expressionis
pola" keduanya berlaku, dan jenis diselesaikan menjadi jenis yang dapat diakses, maka alternatif "relational_expressionis
jenis" harus dipilih.
6.3 Analisis leksikal
6.3.1 Umum
Untuk kenyamanan, tata bahasa leksikal mendefinisikan dan mereferensikan token lexer bernama berikut:
DEFAULT : 'default' ;
NULL : 'null' ;
TRUE : 'true' ;
FALSE : 'false' ;
ASTERISK : '*' ;
SLASH : '/' ;
Meskipun ini adalah aturan lexer, nama-nama ini dieja dalam huruf besar semua untuk membedakannya dari nama aturan lexer biasa.
Catatan: Aturan kenyamanan ini adalah pengecualian untuk praktik biasa untuk tidak memberikan nama token eksplisit untuk token yang ditentukan oleh string harfiah. catatan akhir
Definisi produksi input mendefinisikan struktur leksikal dari unit kompilasi C#.
input
: input_section?
;
input_section
: input_section_part+
;
input_section_part
: input_element* New_Line
| PP_Directive
;
input_element
: Whitespace
| Comment
| token
;
Catatan: Tata bahasa di atas dijelaskan oleh aturan penguraian ANTLR, ini mendefinisikan struktur leksikal unit kompilasi C# dan bukan token leksikal. catatan akhir
Lima elemen dasar membentuk struktur leksikal unit kompilasi C#: Terminator garis (§6.3.2), spasi kosong (§6.3.2)4), komentar (§6.3.3), token (§6.4), dan arahan pra-pemrosesan (§6,5). Dari elemen dasar ini, hanya token yang signifikan dalam tata bahasa sinaks program C# (§6.2.4).
Pemrosesan leksikal unit kompilasi C# terdiri dari mengubah file menjadi urutan token yang menjadi masukan untuk analisis sintaks. Pemutus baris, spasi, dan komentar dapat berfungsi untuk memisahkan token, dan arahan pra-pemrosesan dapat menyebabkan bagian unit kompilasi dilewati, tetapi selain daripada itu, elemen leksikal ini tidak berdampak pada struktur sintaksis program C#.
Ketika beberapa produksi tata bahasa leksikal cocok dengan urutan karakter dalam unit kompilasi, pemrosesan leksikal selalu membentuk elemen leksikal terpanjang yang mungkin.
Contoh: Urutan karakter diproses
//
sebagai awal komentar baris tunggal karena elemen leksikal tersebut lebih panjang dari satu/
token. contoh akhir
Beberapa token didefinisikan oleh sekumpulan aturan leksikal; aturan utama dan satu atau beberapa sub-aturan. Yang terakhir ditandai dalam tata bahasa dengan fragment
untuk menunjukkan bahwa aturan tersebut mendefinisikan bagian dari token lain. Aturan fragmen tidak dipertimbangkan dalam urutan aturan leksikal dari atas ke bawah.
Catatan: Di ANTLR
fragment
adalah kata kunci yang menghasilkan perilaku yang sama yang ditentukan di sini. catatan akhir
Penghenti baris
Pembatas baris membagi karakter unit kompilasi C# menjadi baris.
New_Line
: New_Line_Character
| '\u000D\u000A' // carriage return, line feed
;
Untuk kompatibilitas dengan alat pengeditan kode sumber yang menambahkan penanda akhir file, dan untuk memungkinkan unit kompilasi dilihat sebagai urutan baris yang dihentikan dengan benar, transformasi berikut diterapkan, secara berurutan, ke setiap unit kompilasi dalam program C#:
- Jika karakter terakhir unit kompilasi adalah karakter Control-Z (U+001A), karakter ini akan dihapus.
- Karakter carriage-return (U+000D) ditambahkan ke akhir satuan kompilasi jika satuan kompilasi tersebut tidak kosong dan jika karakter terakhir satuan kompilasi bukan carriage-return (U+000D), umpan baris (U+000A), karakter baris berikutnya (U+0085), pemisah baris (U+2028), atau pemisah paragraf (U+2029).
Catatan: Carriage-return tambahan memungkinkan program berakhir dalam PP_Directive (§6.5) yang tidak memiliki New_Line penghentian. catatan akhir
6.3.3 Komentar
Dua bentuk komentar didukung: komentar berbatas dan komentar baris tunggal.
Komentar yang dibatasi dimulai dengan karakter /*
dan diakhir dengan karakter .*/
Komentar yang dibatasi dapat menempati sebagian baris, satu baris, atau beberapa baris.
Contoh: Contoh
/* Hello, world program This program writes "hello, world" to the console */ class Hello { static void Main() { System.Console.WriteLine("hello, world"); } }
menyertakan komentar yang dibatasi.
contoh akhir
Komentar satu baris dimulai dengan karakter //
dan meluas ke akhir baris.
Contoh: Contoh
// Hello, world program // This program writes "hello, world" to the console // class Hello // any name will do for this class { static void Main() // this method must be named "Main" { System.Console.WriteLine("hello, world"); } }
memperlihatkan beberapa komentar baris tunggal.
contoh akhir
Comment
: Single_Line_Comment
| Delimited_Comment
;
fragment Single_Line_Comment
: '//' Input_Character*
;
fragment Input_Character
// anything but New_Line_Character
: ~('\u000D' | '\u000A' | '\u0085' | '\u2028' | '\u2029')
;
fragment New_Line_Character
: '\u000D' // carriage return
| '\u000A' // line feed
| '\u0085' // next line
| '\u2028' // line separator
| '\u2029' // paragraph separator
;
fragment Delimited_Comment
: '/*' Delimited_Comment_Section* ASTERISK+ '/'
;
fragment Delimited_Comment_Section
: SLASH
| ASTERISK* Not_Slash_Or_Asterisk
;
fragment Not_Slash_Or_Asterisk
: ~('/' | '*') // Any except SLASH or ASTERISK
;
Komentar tidak bersarang. Karakter berurutan /*
dan */
tidak memiliki arti khusus dalam komentar satu baris, dan urutan //
karakter dan /*
tidak memiliki arti khusus dalam komentar yang dibatasi.
Komentar tidak diproses di dalam literal karakter dan string.
Catatan: Aturan ini harus ditafsirkan dengan hati-hati. Misalnya, dalam contoh di bawah ini, komentar yang dibatasi yang dimulai sebelum
A
berakhir antaraB
danC()
. Alasannya adalah bahwa// B */ C();
sebenarnya bukan komentar satu baris, karena
//
tidak memiliki arti khusus dalam komentar yang dibatasi, dan begitu juga*/
memiliki arti khusus yang biasa dalam baris itu.Demikian juga, komentar yang dibatasi dimulai sebelum
D
berakhir sebelumE
. Alasannya adalah bahwa"D */ "
sebenarnya bukan string literal, karena karakter kutipan ganda awal muncul di dalam komentar yang dibatasi.Konsekuensi yang berguna dari
/*
dan*/
tidak memiliki arti khusus dalam komentar satu baris adalah bahwa blok baris kode sumber dapat dikomentari dengan menempatkan//
di awal setiap baris. Secara umum, tidak berfungsi untuk menempatkan/*
di depan baris-baris tersebut dan*/
setelahnya, karena ini tidak merangkum komentar yang dibatasi dengan benar di blok, dan secara umum dapat sepenuhnya mengubah struktur komentar yang dibatasi tersebut.Contoh kode:
static void Main() { /* A // B */ C(); Console.WriteLine(/* "D */ "E"); }
catatan akhir
Single_Line_Comment dan Delimited_Comment yang memiliki format tertentu dapat digunakan sebagai komentar dokumentasi, seperti yang dijelaskan dalam §D.
6.3.4 Spasi kosong
Ruang putih didefinisikan sebagai karakter apa pun dengan kelas Unicode Zs (yang mencakup karakter spasi) serta karakter tab horizontal, karakter tab vertikal, dan karakter pengumpan halaman.
Whitespace
: [\p{Zs}] // any character with Unicode class Zs
| '\u0009' // horizontal tab
| '\u000B' // vertical tab
| '\u000C' // form feed
;
6.4 Token
6.4.1 Umum
Ada beberapa jenis token: pengidentifikasi, kata kunci, literal, operator, dan tanda baca. Spasi kosong dan komentar bukan token, meskipun bertindak sebagai pemisah untuk token.
token
: identifier
| keyword
| Integer_Literal
| Real_Literal
| Character_Literal
| String_Literal
| operator_or_punctuator
;
Catatan: Ini adalah aturan pengurai ANTLR, ini tidak menentukan token leksikal melainkan kumpulan jenis token. catatan akhir
6.4.2 Urutan escape karakter Unicode
Urutan escape Unicode mewakili titik kode Unicode. Sekuen escape Unicode diproses dalam pengidentifikasi (§6.4.3), literal karakter (§6.4.5.5), literal string biasa (§6.4.5.6), dan ekspresi string biasa terinterpolasi (§12.8.3). Urutan escape Unicode tidak diproses di lokasi lain (misalnya, untuk membentuk operator, tanda baca, atau kata kunci).
fragment Unicode_Escape_Sequence
: '\\u' Hex_Digit Hex_Digit Hex_Digit Hex_Digit
| '\\U' Hex_Digit Hex_Digit Hex_Digit Hex_Digit
Hex_Digit Hex_Digit Hex_Digit Hex_Digit
;
Urutan escape karakter Unicode mewakili satu titik kode Unicode yang dibentuk oleh angka heksadesimal setelah karakter "\u" atau "\U". Karena C# menggunakan pengodean 16-bit titik kode Unicode dalam nilai karakter dan string, titik kode Unicode dalam rentang U+10000
U+10FFFF
diwakili menggunakan dua unit kode pengganti Unicode. Poin kode Unicode yang lebih besar dari U+FFFF
tidak diizinkan dalam literal karakter. Poin kode Unicode di atas U+10FFFF
tidak valid dan tidak didukung.
Beberapa terjemahan tidak dilakukan. Misalnya, string literal "\u005Cu005C"
setara dengan "\u005C"
bukan "\"
.
Catatan: Nilai Unicode
\u005C
adalah karakter "\
". catatan akhir
Contoh: Contoh
class Class1 { static void Test(bool \u0066) { char c = '\u0066'; if (\u0066) { System.Console.WriteLine(c.ToString()); } } }
menunjukkan beberapa penggunaan
\u0066
, yang merupakan urutan escape untuk huruf "f
". Program ini setara denganclass Class1 { static void Test(bool f) { char c = 'f'; if (f) { System.Console.WriteLine(c.ToString()); } } }
contoh akhir
6.4.3 Pengidentifikasi
Aturan untuk pengidentifikasi yang diberikan dalam subklasifikasi ini sesuai persis dengan yang direkomendasikan oleh Unicode Standard Annex 15 kecuali bahwa garis bawah diizinkan sebagai karakter awal (seperti tradisional dalam bahasa pemrograman C), urutan escape Unicode diizinkan dalam pengidentifikasi, dan karakter "@
" diizinkan sebagai awalan untuk memungkinkan kata kunci digunakan sebagai pengidentifikasi.
identifier
: Simple_Identifier
| contextual_keyword
;
Simple_Identifier
: Available_Identifier
| Escaped_Identifier
;
fragment Available_Identifier
// excluding keywords or contextual keywords, see note below
: Basic_Identifier
;
fragment Escaped_Identifier
// Includes keywords and contextual keywords prefixed by '@'.
// See note below.
: '@' Basic_Identifier
;
fragment Basic_Identifier
: Identifier_Start_Character Identifier_Part_Character*
;
fragment Identifier_Start_Character
: Letter_Character
| Underscore_Character
;
fragment Underscore_Character
: '_' // underscore
| '\\u005' [fF] // Unicode_Escape_Sequence for underscore
| '\\U0000005' [fF] // Unicode_Escape_Sequence for underscore
;
fragment Identifier_Part_Character
: Letter_Character
| Decimal_Digit_Character
| Connecting_Character
| Combining_Character
| Formatting_Character
;
fragment Letter_Character
// Category Letter, all subcategories; category Number, subcategory letter.
: [\p{L}\p{Nl}]
// Only escapes for categories L & Nl allowed. See note below.
| Unicode_Escape_Sequence
;
fragment Combining_Character
// Category Mark, subcategories non-spacing and spacing combining.
: [\p{Mn}\p{Mc}]
// Only escapes for categories Mn & Mc allowed. See note below.
| Unicode_Escape_Sequence
;
fragment Decimal_Digit_Character
// Category Number, subcategory decimal digit.
: [\p{Nd}]
// Only escapes for category Nd allowed. See note below.
| Unicode_Escape_Sequence
;
fragment Connecting_Character
// Category Punctuation, subcategory connector.
: [\p{Pc}]
// Only escapes for category Pc allowed. See note below.
| Unicode_Escape_Sequence
;
fragment Formatting_Character
// Category Other, subcategory format.
: [\p{Cf}]
// Only escapes for category Cf allowed, see note below.
| Unicode_Escape_Sequence
;
Catatan:
- Untuk informasi tentang kelas karakter Unicode yang disebutkan di atas, lihat Standar Unicode.
- Fragmen Available_Identifier memerlukan pengecualian kata kunci dan kata kunci kontekstual. Jika tata bahasa dalam spesifikasi ini diproses dengan ANTLR maka pengecualian ini ditangani secara otomatis oleh semantik ANTLR:
- Kata kunci dan kata kunci kontekstual terjadi dalam tata bahasa sebagai string harfiah.
- ANTLR membuat aturan token leksikal implisit dibuat dari string harfiah ini.
- ANTLR mempertimbangkan aturan implisit ini sebelum aturan leksikal eksplisit dalam tata bahasa.
- Oleh karena itu, fragmen Available_Identifier tidak akan cocok dengan kata kunci atau kata kunci kontekstual karena aturan leksikal untuk kata-kata tersebut mendahuluinya.
- Fragmen Escaped_Identifier mencakup kata kunci yang dihindari dan kata kunci kontekstual karena merupakan bagian dari token yang lebih panjang yang dimulai dengan
@
dan pemrosesan leksikal selalu membentuk elemen leksikal terpanjang yang memungkinkan (§6.3.1).- Bagaimana implementasi memberlakukan pembatasan pada nilai Unicode_Escape_Sequence yang diizinkan adalah masalah implementasi.
catatan akhir
Contoh: Contoh pengidentifikasi yang valid adalah
identifier1
, ,_identifier2
dan@if
. contoh akhir
Pengidentifikasi dalam program yang sesuai harus dalam format kanonis yang ditentukan oleh Unicode Normalization Form C, seperti yang didefinisikan oleh Unicode Standard Annex 15. Perilaku saat menemukan pengidentifikasi yang tidak dalam Formulir Normalisasi C ditentukan implementasi; namun, diagnostik tidak diperlukan.
Awalan "@
" memungkinkan penggunaan kata kunci sebagai pengidentifikasi, yang berguna saat berinteraksi dengan bahasa pemrograman lainnya. Karakter @
sebenarnya bukan bagian dari pengidentifikasi, sehingga pengidentifikasi mungkin terlihat dalam bahasa lain sebagai pengidentifikasi normal, tanpa awalan. Pengidentifikasi dengan awalan @
disebut pengidentifikasi verbatim.
Catatan: Penggunaan awalan
@
untuk pengidentifikasi yang bukan kata kunci diizinkan, tetapi sangat tidak disarankan sebagai masalah gaya. catatan akhir
Contoh: Contoh:
class @class { public static void @static(bool @bool) { if (@bool) { System.Console.WriteLine("true"); } else { System.Console.WriteLine("false"); } } } class Class1 { static void M() { cl\u0061ss.st\u0061tic(true); } }
mendefinisikan kelas bernama "
class
" dengan metode statis bernama "static
" yang mengambil parameter bernama "bool
". Perhatikan bahwa karena pelarian Unicode tidak diizinkan dalam kata kunci, token "cl\u0061ss
" adalah pengidentifikasi, dan merupakan pengidentifikasi yang sama dengan "@class
".contoh akhir
Dua pengidentifikasi dianggap sama jika identik setelah transformasi berikut diterapkan, secara berurutan:
- Awalan "
@
", jika digunakan, dihapus. - Setiap Unicode_Escape_Sequence diubah menjadi karakter Unicode yang sesuai.
- Setiap Formatting_Character dihapus.
Semantik pengidentifikasi bernama _
tergantung pada konteks di mana pengidentifikasi muncul:
- Ini dapat menunjukkan elemen program bernama, seperti variabel, kelas, atau metode, atau
- Ini dapat menunjukkan pembuangan (§9.2.9.2).
Pengidentifikasi yang berisi dua karakter garis bawah berturut-turut (U+005F
) dicadangkan untuk digunakan oleh implementasi; namun, tidak ada diagnostik yang diperlukan jika pengidentifikasi tersebut ditentukan.
Catatan: Misalnya, implementasi mungkin menyediakan kata kunci yang diperluas yang dimulai dengan dua garis bawah. catatan akhir
6.4.4 Kata Kunci
Kata kunci adalah urutan karakter mirip pengidentifikasi yang dicadangkan, dan tidak dapat digunakan sebagai pengidentifikasi kecuali diawali oleh karakter @
.
keyword
: 'abstract' | 'as' | 'base' | 'bool' | 'break'
| 'byte' | 'case' | 'catch' | 'char' | 'checked'
| 'class' | 'const' | 'continue' | 'decimal' | DEFAULT
| 'delegate' | 'do' | 'double' | 'else' | 'enum'
| 'event' | 'explicit' | 'extern' | FALSE | 'finally'
| 'fixed' | 'float' | 'for' | 'foreach' | 'goto'
| 'if' | 'implicit' | 'in' | 'int' | 'interface'
| 'internal' | 'is' | 'lock' | 'long' | 'namespace'
| 'new' | NULL | 'object' | 'operator' | 'out'
| 'override' | 'params' | 'private' | 'protected' | 'public'
| 'readonly' | 'ref' | 'return' | 'sbyte' | 'sealed'
| 'short' | 'sizeof' | 'stackalloc' | 'static' | 'string'
| 'struct' | 'switch' | 'this' | 'throw' | TRUE
| 'try' | 'typeof' | 'uint' | 'ulong' | 'unchecked'
| 'unsafe' | 'ushort' | 'using' | 'virtual' | 'void'
| 'volatile' | 'while'
;
Kata kunci kontekstual adalah urutan karakter yang mirip identifikator dan memiliki arti khusus dalam konteks tertentu, tetapi tidak dicadangkan, dan dapat digunakan sebagai identifikator di luar konteks tersebut, serta juga dapat digunakan ketika diawali dengan karakter @
.
contextual_keyword
: 'add' | 'alias' | 'ascending' | 'async' | 'await'
| 'by' | 'descending' | 'dynamic' | 'equals' | 'from'
| 'get' | 'global' | 'group' | 'into' | 'join'
| 'let' | 'nameof' | 'notnull' | 'on' | 'orderby'
| 'partial' | 'remove' | 'select' | 'set' | 'unmanaged'
| 'value' | 'var' | 'when' | 'where' | 'yield'
;
Catatan: kata kunci dan kata kunci kontekstual adalah aturan parser karena tidak memperluas jenis token baru. Semua kata kunci dan kata kunci kontekstual didefinisikan oleh aturan leksikal implisit saat terjadi sebagai string harfiah dalam tata bahasa (§6.2.3). catatan akhir
Dalam kebanyakan kasus, lokasi sintaksis kata kunci kontekstual sehingga tidak pernah membingungkan dengan penggunaan pengenal biasa. Misalnya, dalam deklarasi properti, pengidentifikasi get
dan set
memiliki arti khusus (§15.7.3). Pengidentifikasi selain get
atau set
tidak pernah diizinkan di lokasi ini, sehingga penggunaan ini tidak bertentangan dengan penggunaan kata-kata ini sebagai pengidentifikasi.
Dalam kasus tertentu tata bahasa tidak cukup untuk membedakan penggunaan kata kunci kontekstual dari pengidentifikasi. Dalam semua kasus seperti itu akan ditentukan cara membedakan antara keduanya. Misalnya, kata kunci var
kontekstual dalam deklarasi variabel lokal yang diketik secara implisit (§13.6.2) mungkin bertentangan dengan jenis yang dideklarasikan yang disebut var
, dalam hal ini nama yang dinyatakan lebih diutamakan daripada penggunaan pengidentifikasi sebagai kata kunci kontekstual.
Contoh lain disambiguasi tersebut adalah kata kunci await
kontekstual (§12.9.8.1), yang dianggap sebagai kata kunci hanya ketika di dalam metode yang dideklarasikan async
, tetapi dapat digunakan sebagai pengidentifikasi di tempat lain.
Sama seperti kata kunci, kata kunci kontekstual dapat digunakan sebagai pengidentifikasi biasa dengan mengawalinya dengan @
karakter.
Catatan: Saat digunakan sebagai kata kunci kontekstual, pengidentifikasi ini tidak boleh berisi Unicode_Escape_Sequence. catatan akhir
6.4.5 Literal
6.4.5.1 Umum
Literal (§12.8.2) adalah representasi kode sumber dari suatu nilai.
literal
: boolean_literal
| Integer_Literal
| Real_Literal
| Character_Literal
| String_Literal
| null_literal
;
Catatan: literal adalah aturan pengurai karena mengelompokkan jenis token lain dan tidak memperkenalkan jenis token baru. catatan akhir
6.4.5.2 Literal bool
Ada dua nilai harfiah Boolean: true
dan false
.
boolean_literal
: TRUE
| FALSE
;
Catatan: boolean_literal adalah aturan pengurai karena mengelompokkan jenis token lain dan tidak memperkenalkan jenis token baru. catatan akhir
Jenis boolean_literal adalah bool
.
6.4.5.3 Literal bilangan bulat
Literal bilangan bulat digunakan untuk menulis nilai jenis int
, , uint
long
, dan ulong
. Literal bilangan bulat memiliki tiga bentuk yang mungkin: desimal, heksadesimal, dan biner.
Integer_Literal
: Decimal_Integer_Literal
| Hexadecimal_Integer_Literal
| Binary_Integer_Literal
;
fragment Decimal_Integer_Literal
: Decimal_Digit Decorated_Decimal_Digit* Integer_Type_Suffix?
;
fragment Decorated_Decimal_Digit
: '_'* Decimal_Digit
;
fragment Decimal_Digit
: '0'..'9'
;
fragment Integer_Type_Suffix
: 'U' | 'u' | 'L' | 'l' |
'UL' | 'Ul' | 'uL' | 'ul' | 'LU' | 'Lu' | 'lU' | 'lu'
;
fragment Hexadecimal_Integer_Literal
: ('0x' | '0X') Decorated_Hex_Digit+ Integer_Type_Suffix?
;
fragment Decorated_Hex_Digit
: '_'* Hex_Digit
;
fragment Hex_Digit
: '0'..'9' | 'A'..'F' | 'a'..'f'
;
fragment Binary_Integer_Literal
: ('0b' | '0B') Decorated_Binary_Digit+ Integer_Type_Suffix?
;
fragment Decorated_Binary_Digit
: '_'* Binary_Digit
;
fragment Binary_Digit
: '0' | '1'
;
Jenis bilangan bulat literal ditentukan sebagai berikut:
- Jika literal tidak memiliki akhiran, ia memiliki yang pertama dari jenis ini di mana nilainya dapat diwakili:
int
, ,uint
long
,ulong
. - Jika literal diakhiri oleh
U
atauu
, literal tersebut memiliki tipe pertama di mana nilainya dapat diwakili:uint
,ulong
. - Jika literal diakhiri oleh
L
ataul
, literal tersebut memiliki jenis pertama di mana nilainya dapat diwakili:long
,ulong
. - Jika literal diakhiri dengan
UL
,Ul
,uL
,ul
,LU
,Lu
,lU
, ataulu
, itu berjenisulong
.
Jika nilai yang diwakili oleh ulong
bilangan bulat literal berada di luar rentang tipe, kesalahan waktu kompilasi terjadi.
Catatan: Sebagai soal gaya, disarankan bahwa "
L
" digunakan alih-alih "l
" saat menulis literal jenislong
, karena mudah untuk membingungkan huruf "l
" dengan digit "1
". catatan akhir
Untuk mengizinkan sekecil mungkin int
dan long
nilai ditulis sebagai literal bilangan bulat, dua aturan berikut ada:
-
Ketika Integer_Literal yang mewakili nilai
2147483648
(2³¹) dan tidak ada Integer_Type_Suffix muncul sebagai token segera setelah token operator minus unary (§12.9.3), hasilnya (dari kedua token) adalah konstanta tipe int dengan nilai−2147483648
(−2³¹). Dalam semua situasi lain, Integer_Literal semacam itu adalah dari jenisuint
. -
Ketika Integer_Literal yang mewakili nilai
9223372036854775808
(2⁶³) dan tidak ada Integer_Type_Suffix atau Integer_Type_SuffixL
ataul
muncul sebagai token segera setelah token operator minus unary (§12.9.3), hasilnya (dari kedua token) adalah konstanta jenislong
dengan nilai−9223372036854775808
(−2⁶³). Dalam semua situasi lain, Integer_Literal adalah tipeulong
.
Contoh:
123 // decimal, int 10_543_765Lu // decimal, ulong 1_2__3___4____5 // decimal, int _123 // not a numeric literal; identifier due to leading _ 123_ // invalid; no trailing _allowed 0xFf // hex, int 0X1b_a0_44_fEL // hex, long 0x1ade_3FE1_29AaUL // hex, ulong 0x_abc // hex, int _0x123 // not a numeric literal; identifier due to leading _ 0xabc_ // invalid; no trailing _ allowed 0b101 // binary, int 0B1001_1010u // binary, uint 0b1111_1111_0000UL // binary, ulong 0B__111 // binary, int __0B111 // not a numeric literal; identifier due to leading _ 0B111__ // invalid; no trailing _ allowed
contoh akhir
6.4.5.4 Literal bilangan real
Literal nyata digunakan untuk menulis nilai jenis float
, , double
dan decimal
.
Real_Literal
: Decimal_Digit Decorated_Decimal_Digit* '.'
Decimal_Digit Decorated_Decimal_Digit* Exponent_Part? Real_Type_Suffix?
| '.' Decimal_Digit Decorated_Decimal_Digit* Exponent_Part? Real_Type_Suffix?
| Decimal_Digit Decorated_Decimal_Digit* Exponent_Part Real_Type_Suffix?
| Decimal_Digit Decorated_Decimal_Digit* Real_Type_Suffix
;
fragment Exponent_Part
: ('e' | 'E') Sign? Decimal_Digit Decorated_Decimal_Digit*
;
fragment Sign
: '+' | '-'
;
fragment Real_Type_Suffix
: 'F' | 'f' | 'D' | 'd' | 'M' | 'm'
;
Jika tidak ada Real_Type_Suffix yang ditentukan, jenis Real_Literal adalah double
. Jika tidak, Real_Type_Suffix menentukan jenis literal numerik, sebagai berikut:
- Sebuah literal nyata yang diikuti oleh
F
atauf
berjenisfloat
.Contoh: Literal
1f
,1.5f
,1e10f
, dan123.456F
semuanya berjenisfloat
. contoh akhir - Sebuah literal nyata yang berakhiran dengan
D
ataud
adalah dari jenisdouble
.Contoh: Literal ,
1d
,1.5d
, dan1e10d
semuanya berjenis123.456D
double
. contoh akhir - Harfiah nyata yang diakhiri dengan
M
ataum
berjenisdecimal
.Contoh: Literal
1m
,1.5m
,1e10m
, dan123.456M
semuanya berjenisdecimal
. contoh akhir Literal ini dikonversi kedecimal
nilai dengan mengambil nilai yang tepat, dan, jika perlu, membulatkan ke nilai terdekat yang dapat diwakili menggunakan pembulatan bankir (§8.3.8). Setiap skala yang jelas dalam literal dipertahankan kecuali nilai dibulatkan. Catatan: Oleh karena itu, literal2.900m
akan diurai untuk membentukdecimal
dengan tanda0
, koefisien2900
, dan skala3
. catatan akhir
Jika besarnya literal yang ditentukan terlalu besar untuk diwakili dalam jenis yang ditunjukkan, kesalahan waktu kompilasi terjadi.
Catatan: Secara khusus, Real_Literal tidak akan pernah menghasilkan tak terbatas titik mengambang. Namun, Real_Literal bukan nol dapat dibulatkan ke nol. catatan akhir
Nilai harfiah nyata jenis float
atau double
ditentukan dengan menggunakan mode "bulat ke terdekat" IEC 60559 dengan ikatan yang dipecah menjadi "genap" (nilai dengan nol bit-paling signifikan), dan semua digit dianggap signifikan.
Catatan: Dalam literal nyata, digit desimal selalu diperlukan setelah titik desimal. Misalnya,
1.3F
adalah harfiah nyata tetapi1.F
tidak. catatan akhirContoh:
1.234_567 // double .3e5f // float 2_345E-2_0 // double 15D // double 19.73M // decimal 1.F // parsed as a member access of F due to non-digit after . 1_.2F // invalid; no trailing _ allowed in integer part 1._234 // parsed as a member access of _234 due to non-digit after . 1.234_ // invalid; no trailing _ allowed in fraction .3e_5F // invalid; no leading _ allowed in exponent .3e5_F // invalid; no trailing _ allowed in exponent
contoh akhir
6.4.5.5 Literal karakter
Karakter harfiah mewakili satu karakter, dan terdiri dari karakter dalam tanda kutip, seperti dalam 'a'
.
Character_Literal
: '\'' Character '\''
;
fragment Character
: Single_Character
| Simple_Escape_Sequence
| Hexadecimal_Escape_Sequence
| Unicode_Escape_Sequence
;
fragment Single_Character
// anything but ', \, and New_Line_Character
: ~['\\\u000D\u000A\u0085\u2028\u2029]
;
fragment Simple_Escape_Sequence
: '\\\'' | '\\"' | '\\\\' | '\\0' | '\\a' | '\\b' |
'\\f' | '\\n' | '\\r' | '\\t' | '\\v'
;
fragment Hexadecimal_Escape_Sequence
: '\\x' Hex_Digit Hex_Digit? Hex_Digit? Hex_Digit?
;
Catatan: Karakter yang mengikuti karakter garis miring terbalik (
\
) dalam Karakter harus merupakan salah satu karakter berikut:'
,"
,\
0
a
b
f
n
r
,t
,u
, ,U
, ,x
, .v
Jika tidak, terjadi kesalahan waktu kompilasi. catatan akhir
Catatan: Penggunaan
\x
produksi Hexadecimal_Escape_Sequence dapat rentan terhadap kesalahan dan sulit dibaca karena jumlah variabel digit heksadesimal mengikuti\x
. Misalnya, dalam kode:string good = "\x9Good text"; string bad = "\x9Bad text";
Pada awalnya, mungkin tampak bahwa karakter awal sama (
U+0009
, karakter tab) di kedua string. Bahkan string kedua dimulai denganU+9BAD
karena semua tiga huruf dalam kata "Bad" adalah digit heksadesimal yang valid. Sebagai soal gaya, disarankan agar\x
dihindari sebagai gantinya menggunakan urutan escape tertentu (\t
dalam contoh ini) atau urutan escape tetap yang memiliki panjang tetap\u
.catatan akhir
Urutan escape heksadesimal mewakili satu unit kode Unicode UTF-16, dengan nilai yang dibentuk oleh angka heksadesimal berikut "\x
".
Jika nilai yang diwakili oleh karakter literal lebih besar dari U+FFFF
, kesalahan waktu kompilasi terjadi.
Urutan pelarian Unicode (§6.4.2) dalam karakter harfiah harus dalam rentang U+0000
ke U+FFFF
.
Urutan escape sederhana mewakili karakter Unicode, seperti yang dijelaskan dalam tabel di bawah ini.
Rangkaian escape | Nama karakter | Titik kode Unicode |
---|---|---|
\' |
Kutipan tunggal | U+0027 |
\" |
Kutipan ganda | U+0022 |
\\ |
Garis miring terbalik | U+005C |
\0 |
Null | U+0000 |
\a |
Peringatan | U+0007 |
\b |
Backspace | U+0008 |
\f |
Umpan formulir | U+000C |
\n |
Baris baru | U+000A |
\r |
Kembali ke baris baru | U+000D |
\t |
Tab horizontal | U+0009 |
\v |
Tab Vertikal | U+000B |
Jenis Character_Literal adalah char
.
6.4.5.6 String literal
C# mendukung dua bentuk literal string: literal string reguler dan literal string verbatim. String reguler literal terdiri dari nol atau lebih karakter yang diapit dalam tanda kutip ganda, seperti dalam "hello"
, dan dapat menyertakan urutan escape sederhana (seperti \t
untuk karakter tab), dan urutan escape heksadesimal dan Unicode.
String literal verbatim terdiri atas karakter @
diikuti oleh karakter tanda kutip ganda, nol atau lebih karakter, dan karakter tanda kutip ganda penutup.
Contoh: Contoh sederhananya adalah
@"hello"
. contoh akhir
Dalam string verbatim harfiah, karakter antara pemisah ditafsirkan verbatim, dengan satu-satunya pengecualian adalah Quote_Escape_Sequence, yang mewakili satu karakter kutipan ganda. Secara khusus, karakter escape sederhana, serta karakter escape heksadesimal dan Unicode tidak diproses dalam string literal verbatim. String verbatim harfiah dapat mencakup beberapa baris.
String_Literal
: Regular_String_Literal
| Verbatim_String_Literal
;
fragment Regular_String_Literal
: '"' Regular_String_Literal_Character* '"'
;
fragment Regular_String_Literal_Character
: Single_Regular_String_Literal_Character
| Simple_Escape_Sequence
| Hexadecimal_Escape_Sequence
| Unicode_Escape_Sequence
;
fragment Single_Regular_String_Literal_Character
// anything but ", \, and New_Line_Character
: ~["\\\u000D\u000A\u0085\u2028\u2029]
;
fragment Verbatim_String_Literal
: '@"' Verbatim_String_Literal_Character* '"'
;
fragment Verbatim_String_Literal_Character
: Single_Verbatim_String_Literal_Character
| Quote_Escape_Sequence
;
fragment Single_Verbatim_String_Literal_Character
: ~["] // anything but quotation mark (U+0022)
;
fragment Quote_Escape_Sequence
: '""'
;
Contoh: Contoh
string a = "Happy birthday, Joel"; // Happy birthday, Joel string b = @"Happy birthday, Joel"; // Happy birthday, Joel string c = "hello \t world"; // hello world string d = @"hello \t world"; // hello \t world string e = "Joe said \"Hello\" to me"; // Joe said "Hello" to me string f = @"Joe said ""Hello"" to me"; // Joe said "Hello" to me string g = "\\\\server\\share\\file.txt"; // \\server\share\file.txt string h = @"\\server\share\file.txt"; // \\server\share\file.txt string i = "one\r\ntwo\r\nthree"; string j = @"one two three";
menunjukkan berbagai string literal. String terakhir harfiah,
j
, adalah string verbatim literal yang mencakup beberapa baris. Karakter di antara tanda kutip, termasuk spasi putih seperti karakter baris baru, dipertahankan verbatim, dan setiap pasangan karakter kutipan ganda digantikan oleh satu karakter tersebut.contoh akhir
Catatan: Setiap hentian baris dalam literal string verbatim adalah bagian dari string yang dihasilkan. Jika karakter yang tepat yang digunakan untuk membentuk pemisah baris secara semantik relevan dengan aplikasi, alat apa pun yang menerjemahkan pemisah baris dalam kode sumber ke format yang berbeda (antara "
\n
" dan "\r\n
", misalnya) akan mengubah perilaku aplikasi. Pengembang harus berhati-hati dalam situasi seperti itu. catatan akhir
Catatan: Karena urutan escape heksadesimal dapat memiliki jumlah variabel digit heksa, string literal
"\x123"
berisi satu karakter dengan nilai123
heksa . Untuk membuat string yang berisi karakter dengan nilai12
hex diikuti oleh karakter3
, seseorang dapat menulis"\x00123"
atau"\x12"
+"3"
sebagai gantinya. catatan akhir
Jenis String_Literal adalah string
.
Setiap string literal tidak selalu menghasilkan instans string baru. Ketika dua literal string atau lebih yang setara sesuai dengan operator kesetaraan string (§12.12.8), muncul dalam rakitan yang sama, literal string ini mengacu pada instans string yang sama.
Contoh: Misalnya, output yang dihasilkan oleh
class Test { static void Main() { object a = "hello"; object b = "hello"; System.Console.WriteLine(a == b); } }
adalah
True
karena dua literal mengacu pada instans string yang sama.contoh akhir
6.4.5.7 Literal null
null_literal
: NULL
;
Catatan: null_literal adalah aturan pengurai karena tidak memperkenalkan jenis token baru. catatan akhir
null_literal mewakili null
nilai. Ini tidak memiliki tipe, tetapi dapat dikonversi ke tipe referensi mana pun atau tipe nilai yang bisa kosong melalui konversi literal null (§10.2.7).
6.4.6 Operator dan tanda baca
Ada beberapa jenis operator dan tanda baca. Operator digunakan dalam ekspresi untuk menjelaskan operasi yang melibatkan satu atau beberapa operan.
Contoh: Ekspresi
a + b
menggunakan+
operator untuk menambahkan dua operanda
danb
. contoh akhir
Tanda baca adalah untuk pengelompokan dan pemisahan.
operator_or_punctuator
: '{' | '}' | '[' | ']' | '(' | ')' | '.' | ',' | ':' | ';'
| '+' | '-' | ASTERISK | SLASH | '%' | '&' | '|' | '^' | '!' | '~'
| '=' | '<' | '>' | '?' | '??' | '::' | '++' | '--' | '&&' | '||'
| '->' | '==' | '!=' | '<=' | '>=' | '+=' | '-=' | '*=' | '/=' | '%='
| '&=' | '|=' | '^=' | '<<' | '<<=' | '=>' | '??='
;
right_shift
: '>' '>'
;
right_shift_assignment
: '>' '>='
;
Catatan: right_shift dan right_shift_assignment adalah aturan pengurai karena tidak memperkenalkan jenis token baru tetapi mewakili urutan dua token. Aturan operator_or_punctuator hanya ada untuk tujuan deskriptif dan tidak digunakan di tempat lain dalam tata bahasa. catatan akhir
right_shift terdiri dari dua token >
dan >
. Demikian pula, right_shift_assignment terdiri dari dua token >
dan >=
. Tidak seperti produksi lain dalam tata bahasa sinaks, tidak ada karakter apa pun (bahkan spasi putih) yang diizinkan antara dua token di masing-masing produksi ini. Produksi ini diperlakukan khusus untuk memungkinkan penanganan type_parameter_lists yang benar (§15.2.3).
Catatan: Sebelum penambahan generik ke C#,
>>
dan>>=
keduanya adalah token tunggal. Namun, sintaks untuk generik menggunakan karakter pembatas<
dan>
untuk memisahkan parameter jenis dan argumen jenis. Sering kali diinginkan untuk menggunakan jenis konstruksi berlapis, sepertiList<Dictionary<string, int>>
. Daripada mengharuskan programmer untuk memisahkan>
dan>
dengan spasi, definisi dua operator_or_punctuators diubah. catatan akhir
6.5 Arahan pra-pemrosesan
6.5.1 Umum
Arahan pra-pemrosesan memberikan kemampuan untuk melewatkan bagian unit kompilasi secara kondisional, untuk melaporkan kesalahan dan kondisi peringatan, untuk menggambarkan wilayah kode sumber yang berbeda, dan untuk mengatur konteks yang dapat diubah ke null.
Catatan: Istilah "arahan pra-pemrosesan" hanya digunakan untuk konsistensi dengan bahasa pemrograman C dan C++. Di C#, tidak ada langkah pra-pemrosesan terpisah; arahan pra-pemrosesan diproses sebagai bagian dari fase analisis leksikal. catatan akhir
PP_Directive
: PP_Start PP_Kind PP_New_Line
;
fragment PP_Kind
: PP_Declaration
| PP_Conditional
| PP_Line
| PP_Diagnostic
| PP_Region
| PP_Pragma
| PP_Nullable
;
// Only recognised at the beginning of a line
fragment PP_Start
// See note below.
: { getCharPositionInLine() == 0 }? PP_Whitespace? '#' PP_Whitespace?
;
fragment PP_Whitespace
: ( [\p{Zs}] // any character with Unicode class Zs
| '\u0009' // horizontal tab
| '\u000B' // vertical tab
| '\u000C' // form feed
)+
;
fragment PP_New_Line
: PP_Whitespace? Single_Line_Comment? New_Line
;
Catatan:
- Tata bahasa pra-prosesor mendefinisikan satu token
PP_Directive
leksikal yang digunakan untuk semua arahan pra-pemrosesan. Semantik dari masing-masing arahan pra-pemrosesan didefinisikan dalam spesifikasi bahasa ini tetapi bukan cara mengimplementasikannya.PP_Start
Fragmen hanya boleh dikenali pada awal baris,getCharPositionInLine() == 0
predikat leksikal ANTLR di atas menunjukkan salah satu cara di mana ini dapat dicapai dan hanya informatif, implementasi dapat menggunakan strategi yang berbeda.catatan akhir
Direktif pra-pemrosesan berikut tersedia:
-
#define
dan#undef
, yang digunakan untuk mendefinisikan dan tidak mendefinisikan, masing-masing, simbol kompilasi bersyarkat (§6.5.4). -
#if
, ,#elif
#else
, dan#endif
, yang digunakan untuk melewati bagian kode sumber secara kondisional (§6.5.5). -
#line
, yang digunakan untuk mengontrol nomor baris yang dipancarkan untuk kesalahan dan peringatan (§6.5.8). -
#error
, yang digunakan untuk menampilkan kesalahan (§6.5.6). -
#region
dan#endregion
, yang digunakan untuk secara eksplisit menandai bagian kode sumber (§6.5.7). -
#nullable
, yang digunakan untuk menentukan konteks nullable (§6.5.9). -
#pragma
, yang digunakan untuk menentukan informasi kontekstual opsional ke pengkompilasi (§6.5.10).
Arahan pra-pemrosesan selalu menempati baris kode sumber terpisah dan selalu dimulai dengan #
karakter dan nama direktif pra-pemrosesan. Spasi kosong dapat muncul sebelum karakter #
dan antara karakter #
dan nama direktif.
Baris sumber yang berisi direktif #define
, #undef
, #if
, #elif
, #else
, #endif
, #line
, #endregion
, atau #nullable
dapat berakhir dengan komentar baris tunggal. Komentar yang dibatasi ( /* */
gaya komentar) tidak diizinkan pada baris sumber yang berisi arahan pra-pemrosesan.
Arahan pra-pemrosesan bukan bagian dari tata bahasa sinaks C#. Namun, arahan pra-pemrosesan dapat digunakan untuk menyertakan atau mengecualikan urutan token dan dengan cara itu dapat memengaruhi arti program C#.
Contoh: Saat dikompilasi, program
#define A #undef B class C { #if A void F() {} #else void G() {} #endif #if B void H() {} #else void I() {} #endif }
menghasilkan urutan token yang sama persis dengan program
class C { void F() {} void I() {} }
Dengan demikian, sedangkan secara leksikal, kedua program tersebut cukup berbeda, secara sintis, mereka identik.
contoh akhir
6.5.2 Simbol kompilasi kondisional
Fungsionalitas kompilasi kondisional yang disediakan oleh #if
direktif , #elif
, #else
, dan #endif
dikontrol melalui ekspresi pra-pemrosesan (§6.5.3) dan simbol kompilasi kondisional.
fragment PP_Conditional_Symbol
// Must not be equal to tokens TRUE or FALSE. See note below.
: Basic_Identifier
;
Catatan Bagaimana implementasi memberlakukan pembatasan pada nilai Basic_Identifier yang diizinkan adalah masalah implementasi. catatan akhir
Dua simbol kompilasi kondisional dianggap sama jika identik setelah transformasi berikut diterapkan, secara berurutan:
- Setiap Unicode_Escape_Sequence diubah menjadi karakter Unicode yang sesuai.
- Setiap Formatting_Characters dihapus.
Simbol kompilasi kondisional memiliki dua kemungkinan status: didefinisikan atau tidak terdefinisi. Pada awal pemrosesan leksikal unit kompilasi, simbol kompilasi kondisional tidak terdefinisi kecuali telah secara eksplisit didefinisikan oleh mekanisme eksternal (seperti opsi kompilator baris perintah). Ketika direktif #define
diproses, simbol kompilasi bersyarat yang dinamai dalam direktif tersebut didefinisikan dalam unit kompilasi tersebut. Simbol tetap didefinisikan sampai direktif #undef
untuk simbol yang sama diproses, atau sampai akhir unit kompilasi tercapai. Implikasi dari ini adalah bahwa #define
dan #undef
arahan dalam satu unit kompilasi tidak berpengaruh pada unit kompilasi lain dalam program yang sama.
Ketika direferensikan dalam ekspresi pra-pemrosesan (§6.5.3), simbol kompilasi bersyarat yang ditentukan memiliki nilai true
Boolean , dan simbol kompilasi bersyarat yang tidak ditentukan memiliki nilai false
Boolean . Tidak ada persyaratan bahwa simbol kompilasi bersyarat dideklarasikan secara eksplisit sebelum direferensikan dalam ekspresi pra-pemrosesan. Sebaliknya, simbol yang tidak dinyatakan hanya tidak terdefinisi dan dengan demikian memiliki nilai false
.
Namespace untuk simbol kompilasi kondisional berbeda dan terpisah dari semua entitas bernama lainnya dalam program C#. Simbol kompilasi bersyarat hanya dapat direferensikan dalam direktif #define
dan #undef
serta dalam ekspresi pra-pemrosesan.
6.5.3 Ekspresi pra-pemrosesan
Ekspresi pra-pemrosesan dapat terjadi dalam direktif #if
dan #elif
. Operator !
(hanya prefiks negasi logis), ==
, , !=
&&
, , dan ||
diizinkan dalam ekspresi pra-pemrosesan, dan tanda kurung dapat digunakan untuk pengelompokan.
fragment PP_Expression
: PP_Whitespace? PP_Or_Expression PP_Whitespace?
;
fragment PP_Or_Expression
: PP_And_Expression (PP_Whitespace? '||' PP_Whitespace? PP_And_Expression)*
;
fragment PP_And_Expression
: PP_Equality_Expression (PP_Whitespace? '&&' PP_Whitespace?
PP_Equality_Expression)*
;
fragment PP_Equality_Expression
: PP_Unary_Expression (PP_Whitespace? ('==' | '!=') PP_Whitespace?
PP_Unary_Expression)*
;
fragment PP_Unary_Expression
: PP_Primary_Expression
| '!' PP_Whitespace? PP_Unary_Expression
;
fragment PP_Primary_Expression
: TRUE
| FALSE
| PP_Conditional_Symbol
| '(' PP_Whitespace? PP_Expression PP_Whitespace? ')'
;
Ketika direferensikan dalam ekspresi pra-pemrosesan, simbol kompilasi bersyarat yang ditentukan memiliki nilai true
Boolean , dan simbol kompilasi bersyarat yang tidak terdefinisi memiliki nilai false
Boolean .
Evaluasi ekspresi pra-pemrosesan selalu menghasilkan nilai Boolean. Aturan evaluasi untuk ekspresi pra-pemrosesan sama dengan yang untuk ekspresi konstanta (§12.23), kecuali bahwa satu-satunya entitas yang ditentukan pengguna yang dapat direferensikan adalah simbol kompilasi kondisional.
6.5.4 Direktif definisi
Direktif definisi digunakan untuk menentukan atau tidak menentukan simbol kompilasi bersyarkat.
fragment PP_Declaration
: 'define' PP_Whitespace PP_Conditional_Symbol
| 'undef' PP_Whitespace PP_Conditional_Symbol
;
Pemrosesan arahan #define
menyebabkan simbol kompilasi bersyarat yang diberikan menjadi terdefinisi, dimulai dengan baris sumber yang mengikuti arahan tersebut. Demikian juga, pemrosesan arahan #undef
menyebabkan simbol kompilasi bersyarkat yang diberikan menjadi tidak terdefinisi, dimulai dengan baris sumber yang mengikuti arahan.
Setiap #define
dan #undef
arahan dalam unit kompilasi harus muncul sebelum token pertama (§6.4) di unit kompilasi; jika tidak, kesalahan waktu kompilasi terjadi. Dalam istilah intuitif, #define
dan #undef
arahan harus mendahului "kode nyata" di unit kompilasi.
Contoh: Contoh:
#define Enterprise #if Professional || Enterprise #define Advanced #endif namespace Megacorp.Data { #if Advanced class PivotTable {...} #endif }
Sah karena arahan
#define
terletak sebelum token pertama (namespace
kata kunci) di unit kompilasi.contoh akhir
Contoh: Contoh berikut menghasilkan kesalahan waktu kompilasi karena #define mengikuti kode nyata:
#define A namespace N { #define B #if B class Class1 {} #endif }
contoh akhir
#define
dapat mendefinisikan simbol kompilasi kondisional yang sudah ditentukan, tanpa ada intervensi #undef
untuk simbol itu.
Contoh: Contoh di bawah ini mendefinisikan simbol kompilasi bersyarah A lalu mendefinisikannya lagi.
#define A #define A
Untuk kompilator yang memungkinkan simbol kompilasi bersyarkat didefinisikan sebagai opsi kompilasi, cara alternatif untuk pendefinisian ulang tersebut terjadi adalah dengan menentukan simbol sebagai opsi kompilator serta di sumbernya.
contoh akhir
#undef
dapat "menghapus definisi" simbol kompilasi bersyarat yang tidak didefinisikan.
Contoh: Contoh di bawah ini mendefinisikan simbol kompilasi kondisional dan kemudian mendefinisikan ulang dua kali; meskipun yang kedua tidak berpengaruh, masih tetap valid.
#define A #undef A #undef A
contoh akhir
6.5.5 Arahan kompilasi bersyarat
Arahan kompilasi kondisional digunakan untuk menyertakan atau mengecualikan bagian unit kompilasi secara kondisional.
fragment PP_Conditional
: PP_If_Section
| PP_Elif_Section
| PP_Else_Section
| PP_Endif
;
fragment PP_If_Section
: 'if' PP_Whitespace PP_Expression
;
fragment PP_Elif_Section
: 'elif' PP_Whitespace PP_Expression
;
fragment PP_Else_Section
: 'else'
;
fragment PP_Endif
: 'endif'
;
Arahan kompilasi bersyarat harus ditulis dalam kelompok yang terdiri dari, dalam urutan, satu direktif #if
, nol atau lebih direktif #elif
, nol atau satu direktif #else
, dan satu direktif #endif
. Di antara direktif tersebut terdapat bagian bersyarat dari kode sumber. Setiap bagian dikendalikan oleh arahan yang langsung sebelumnya. Bagian kondisional mungkin berisi arahan kompilasi kondisional berlapis asalkan arahan ini membentuk grup lengkap.
Paling banyak salah satu bagian kondisional yang terkandung dipilih untuk pemrosesan leksikal normal:
- Ekspresi-ekspresi dalam direktif dan
#elif
dievaluasi secara berurutan sampai salah satunya menghasilkantrue
. Jika ekspresi menghasilkantrue
, bagian bersyarat yang mengikuti direktif terkait dipilih. - Jika semua PP_Expression menghasilkan
false
, dan jika direktif#else
ada, bagian bersyarat yang mengikuti direktif#else
dipilih. - Jika tidak, tidak ada bagian bersyarat yang dipilih.
Bagian kondisional yang dipilih, jika ada, diproses sebagai input_section normal: kode sumber yang terkandung di bagian harus mematuhi tata bahasa leksikal; token dihasilkan dari kode sumber di bagian; dan arahan pra-pemrosesan di bagian memiliki efek yang ditentukan.
Setiap bagian kondisi yang tersisa dilewatkan dan tidak ada token yang dihasilkan dari kode sumber, kecuali untuk token yang terkait dengan arahan pra-pemrosesan. Oleh karena itu kode sumber yang dilewati, kecuali arahan pra-pemrosesan, mungkin salah secara leksikal. Arahan pra-pemrosesan yang diabaikan harus benar secara leksikal tetapi selain itu tidak diproses. Dalam bagian kondisional yang sedang dilewati bagian kondisional berlapis (terkandung dalam konstruksi berlapis #if...#endif
) juga dilewati.
Catatan: Tata bahasa di atas tidak menangkap pemahaman bahwa bagian kondisional antara direktif pra-pemrosesan mungkin bentuknya tidak benar secara leksikal. Oleh karena itu tata bahasa tidak siap ANTLR karena hanya mendukung input yang benar secara leksikal. catatan akhir
Contoh: Contoh berikut mengilustrasikan bagaimana arahan kompilasi kondisional dapat bersarang:
#define Debug // Debugging on #undef Trace // Tracing off class PurchaseTransaction { void Commit() { #if Debug CheckConsistency(); #if Trace WriteToLog(this.ToString()); #endif #endif CommitHelper(); } ... }
Kecuali dengan arahan pra-pemrosesan, kode sumber yang diabaikan tidak tunduk pada analisis leksikal. Misalnya, berikut ini sah meskipun komentar tidak berakhir di bagian
#else
.#define Debug // Debugging on class PurchaseTransaction { void Commit() { #if Debug CheckConsistency(); #else /* Do something else #endif } ... }
Namun, perhatikan bahwa arahan pra-pemrosesan harus benar secara leksikal bahkan di bagian kode sumber yang dilewati.
Arahan pra-pemrosesan tidak diproses ketika muncul di dalam elemen input multibaris. Misalnya, program:
class Hello { static void Main() { System.Console.WriteLine(@"hello, #if Debug world #else Nebraska #endif "); } }
menghasilkan output:
hello, #if Debug world #else Nebraska #endif
Dalam kasus yang aneh, serangkaian arahan pra-pemrosesan yang diproses mungkin tergantung pada evaluasi pp_expression. Contohnya:
#if X /* #else /* */ class Q { } #endif
selalu menghasilkan aliran token yang sama (
class
Q
{
}
), terlepas dari apakah didefinisikan atau tidak.X
JikaX
didefinisikan, satu-satunya arahan yang diproses adalah#if
dan#endif
, karena komentar multibaris. JikaX
tidak terdefinisi, maka tiga arahan (#if
,#else
,#endif
) adalah bagian dari set direktif.contoh akhir
6.5.6 Arahan diagnostik
Arahan diagnostik digunakan untuk menghasilkan pesan kesalahan dan peringatan secara eksplisit yang dilaporkan dengan cara yang sama seperti kesalahan dan peringatan waktu kompilasi lainnya.
fragment PP_Diagnostic
: 'error' PP_Message?
| 'warning' PP_Message?
;
fragment PP_Message
: PP_Whitespace Input_Character*
;
Contoh: Contoh
#if Debug && Retail #error A build can't be both debug and retail #endif class Test {...}
menghasilkan kesalahan waktu kompilasi ("Suatu build tidak dapat menjadi debug dan rilis sekaligus") jika simbol kompilasi bersyarat
Debug
danRetail
keduanya didefinisikan. Perhatikan bahwa PP_Message dapat berisi teks arbitrer; khususnya, tidak perlu berisi token yang terbentuk dengan baik, seperti yang ditunjukkan oleh kutipan tunggal dalam katacan't
.contoh akhir
6.5.7 Arahan wilayah
Arahan wilayah digunakan untuk menandai wilayah kode sumber secara eksplisit.
fragment PP_Region
: PP_Start_Region
| PP_End_Region
;
fragment PP_Start_Region
: 'region' PP_Message?
;
fragment PP_End_Region
: 'endregion' PP_Message?
;
Tidak ada arti semantik yang melekat pada suatu wilayah; wilayah ditujukan untuk digunakan oleh pemrogram atau oleh alat otomatis untuk menandai bagian kode sumber. Akan ada satu #endregion
arahan yang cocok dengan setiap #region
arahan. Pesan yang ditentukan dalam direktif #region
atau #endregion
juga tidak memiliki arti semantik; itu hanya berfungsi untuk mengidentifikasi wilayah. Arahan #region
dan #endregion
yang dipasangkan mungkin memiliki PP_Message yang berbeda.
Pemrosesan leksikal suatu wilayah:
#region
...
#endregion
sesuai persis dengan pemrosesan leksikal dari perintah kompilasi bersyarat dalam bentuk:
#if true
...
#endif
Catatan: Ini berarti bahwa suatu wilayah dapat menyertakan satu atau beberapa
#if
/.../#endif
, atau terkandung dalam bagian bersyarat dalam#if
/.../#endif
; tetapi wilayah tidak dapat tumpang tindih dengan hanya sebagian dari#if
/.../#endif
, atau mulai dan berakhir di bagian kondisi yang berbeda. catatan akhir
6.5.8 Arahan baris
Arahan baris dapat digunakan untuk mengubah nomor baris dan nama unit kompilasi yang dilaporkan oleh kompilator dalam output seperti peringatan dan kesalahan. Nilai-nilai ini juga digunakan oleh atribut caller-info (§22.5.6).
Catatan: Arahan baris paling umum digunakan dalam alat pemrograman meta yang menghasilkan kode sumber C# dari beberapa input teks lainnya. catatan akhir
fragment PP_Line
: 'line' PP_Whitespace PP_Line_Indicator
;
fragment PP_Line_Indicator
: Decimal_Digit+ PP_Whitespace PP_Compilation_Unit_Name
| Decimal_Digit+
| DEFAULT
| 'hidden'
;
fragment PP_Compilation_Unit_Name
: '"' PP_Compilation_Unit_Name_Character* '"'
;
fragment PP_Compilation_Unit_Name_Character
// Any Input_Character except "
: ~('\u000D' | '\u000A' | '\u0085' | '\u2028' | '\u2029' | '"')
;
Ketika tidak ada #line
direktif yang ada, kompilator melaporkan nomor baris benar dan nama unit kompilasi dalam outputnya. Saat memproses arahan #line
yang menyertakan PP_Line_Indicator yang bukan default
, pengkompilasi memperlakukan baris setelah direktif memiliki nomor baris yang diberikan (dan nama unit kompilasi, jika ditentukan).
Nilai maksimum yang diizinkan untuk Decimal_Digit+
ditentukan implementasi.
Direktif #line default
membatalkan efek dari semua arahan #line
sebelumnya. Kompilator melaporkan informasi baris yang benar untuk baris berikutnya, tepatnya seolah-olah tidak ada #line
arahan yang telah diproses.
Direktif #line hidden
tidak berpengaruh pada unit kompilasi dan nomor baris yang dilaporkan dalam pesan kesalahan, atau dihasilkan oleh penggunaan CallerLineNumberAttribute
(§22.5.6.2). Ini dimaksudkan untuk memengaruhi alat debug tingkat sumber sehingga, saat debug, semua baris antara direktif #line hidden
dan direktif berikutnya #line
(yang bukan #line hidden
) tidak memiliki informasi nomor baris, dan dilewati sepenuhnya saat melangkah melalui kode.
Catatan: Meskipun PP_Compilation_Unit_Name mungkin berisi teks yang terlihat seperti urutan escape, teks tersebut bukan urutan escape; dalam konteks ini karakter '
\
' hanya menunjuk karakter garis miring terbalik biasa. catatan akhir
6.5.9 Direktif nullable
Direktif nullable mengendalikan konteks bernilai nol, seperti yang dijelaskan berikut ini.
fragment PP_Nullable
: 'nullable' PP_Whitespace PP_Nullable_Action
(PP_Whitespace PP_Nullable_Target)?
;
fragment PP_Nullable_Action
: 'disable'
| 'enable'
| 'restore'
;
fragment PP_Nullable_Target
: 'warnings'
| 'annotations'
;
Direktif null menetapkan bendera yang tersedia untuk baris kode berikutnya, sampai direktif null lainnya mengambil alihnya, atau sampai akhir kompilasi _unit tercapai. Konteks nullable berisi dua bendera: anotasi dan peringatan. Efek dari setiap bentuk direktif nullable adalah, sebagai berikut:
-
#nullable disable
: Menonaktifkan baik anotasi nullable maupun peringatan nullable. -
#nullable enable
: Mengaktifkan anotasi nullable dan bendera peringatan nullable. -
#nullable restore
: Memulihkan bendera anotasi dan peringatan ke status yang ditentukan oleh mekanisme eksternal, jika ada. -
#nullable disable annotations
: Menonaktifkan bendera anotasi nullable. Bendera peringatan nullable tidak terpengaruh. -
#nullable enable annotations
: Mengaktifkan bendera anotasi nullable. Bendera peringatan nullable tidak terpengaruh. -
#nullable restore annotations
: Memulihkan penanda anotasi yang dapat bernilai null ke keadaan yang ditentukan oleh mekanisme eksternal, jika ada. Bendera peringatan nullable tidak terpengaruh. -
#nullable disable warnings
: Menonaktifkan bendera peringatan nullable. Indikator anotasi nullable tidak terpengaruh. -
#nullable enable warnings
: Mengaktifkan bendera peringatan nullable. Penanda anotasi nullable tidak terpengaruh. -
#nullable restore warnings
: Memulihkan tanda peringatan yang dapat bernilai null ke status yang ditentukan oleh mekanisme eksternal, jika ada. Penanda anotasi nullable tidak terpengaruh.
Status ekspresi nullable dilacak setiap saat. Status bendera anotasi dan ada atau tidak adanya anotasi yang dapat diubah ke null, ?
, menentukan status null awal dari deklarasi variabel. Peringatan hanya dikeluarkan ketika bendera peringatan diaktifkan.
Contoh: Contoh
#nullable disable string x = null; string y = ""; #nullable enable Console.WriteLine(x.Length); // Warning Console.WriteLine(y.Length);
menghasilkan peringatan waktu kompilasi ("sebagai
x
adalahnull
"). Keadaan nullablex
dipantau di semua tempat. Peringatan dikeluarkan ketika bendera peringatan diaktifkan.contoh akhir
6.5.10 Arahan Pragma
Arahan #pragma
pra-pemrosesan digunakan untuk menentukan informasi kontekstual ke kompilator.
Catatan: Misalnya, pengkompilasi mungkin memberikan
#pragma
arahan yang
- Aktifkan atau nonaktifkan pesan peringatan tertentu saat mengkompilasi kode berikutnya.
- Tentukan pengoptimalan mana yang akan diterapkan ke kode berikutnya.
- Tentukan informasi yang akan digunakan oleh debugger.
catatan akhir
fragment PP_Pragma
: 'pragma' PP_Pragma_Text?
;
fragment PP_Pragma_Text
: PP_Whitespace Input_Character*
;
Input_Characterdalam PP_Pragma_Text ditafsirkan oleh kompiler sesuai dengan implementasi yang ditentukan. Informasi yang disediakan dalam instruksi #pragma
tidak akan mengubah semantik program. Arahan #pragma
hanya akan mengubah perilaku pengkompilasi yang berada di luar cakupan spesifikasi bahasa ini. Jika kompilator tidak dapat menafsirkan Input_Character, pengkompilasi dapat menghasilkan peringatan; namun, itu tidak akan menghasilkan kesalahan waktu kompilasi.
Catatan: PP_Pragma_Text dapat berisi teks arbitrer; khususnya, tidak perlu berisi token yang terbentuk dengan baik. catatan akhir
ECMA C# draft specification