Bagikan melalui


Bekerja dengan sintaks

Pohon sintaks adalah struktur data mendasar yang tidak dapat diubah yang diekspos oleh API kompilator. Pohon-pohon ini mewakili struktur leksikal dan sintik kode sumber. Mereka melayani dua tujuan penting:

  • Untuk mengizinkan alat - seperti IDE, add-in, alat analisis kode, dan perubahan struktur kode - untuk mengamati dan memproses struktur sintaksis kode sumber dalam proyek pengguna.
  • Untuk mengaktifkan alat - seperti pemfaktoran ulang dan IDE - untuk membuat, memodifikasi, dan mengatur ulang kode sumber secara alami tanpa harus menggunakan pengeditan teks langsung. Dengan membuat dan memanipulasi pohon, alat dapat dengan mudah membuat dan mengatur ulang kode sumber.

Pohon sintaksis

Pohon sintaks adalah struktur utama yang digunakan untuk kompilasi, analisis kode, pengikatan, pemfaktoran ulang, fitur IDE, dan pembuatan kode. Tidak ada bagian dari kode sumber yang dipahami tanpa pertama kali diidentifikasi dan dikategorikan ke dalam salah satu dari banyak elemen bahasa struktural terkenal.

Nota

RoslynQuoter adalah alat sumber terbuka yang menunjukkan panggilan API pabrik sintaksis yang digunakan untuk membangun pohon sintaks program. Untuk mencobanya secara langsung, lihat http://roslynquoter.azurewebsites.net.

Pohon sintaks memiliki tiga atribut utama:

  • Mereka menyimpan semua informasi sumber dalam keakuratan penuh. Keakuratan penuh berarti bahwa pohon sintaksis berisi setiap informasi yang ditemukan dalam teks sumber, setiap konstruksi tata bahasa, setiap token leksikal, dan segala sesuatu yang lain di antaranya, termasuk ruang putih, komentar, dan arahan praprosedur. Misalnya, setiap literal yang disebutkan dalam sumber diwakili persis seperti yang ditik. Pohon sintaksis juga menangkap kesalahan dalam kode sumber ketika program tidak lengkap atau salah bentuk dengan mewakili token yang dilewati atau hilang.
  • Mereka dapat menghasilkan teks persis seperti teks asal yang mereka uraikan. Dari simpul sintaks apa pun, dimungkinkan untuk mendapatkan representasi teks subtree yang berakar pada simpul tersebut. Kemampuan ini berarti bahwa pohon sintaksis dapat digunakan sebagai cara untuk membangun dan mengedit teks sumber. Dengan membuat pohon yang Anda miliki, dengan sendirinya, berarti membuat teks yang setara, dan dengan membuat pohon baru dari perubahan pada pohon yang sudah ada, Anda telah mengedit teks secara efektif.
  • Mereka tidak dapat diubah dan aman untuk multi thread. Setelah pohon kode diperoleh, ini adalah cuplikan dari status kode saat ini dan ini tidak akan pernah berubah. Ini memungkinkan beberapa pengguna berinteraksi dengan pohon sintaks yang sama pada saat yang sama di utas yang berbeda tanpa penguncian atau duplikasi. Karena pohon tidak dapat diubah dan tidak ada modifikasi yang dapat dilakukan langsung ke pohon, metode pabrik membantu membuat dan memodifikasi pohon sintaksis dengan membuat rekam jepret pohon tambahan. Pohon-pohon efisien dalam cara mereka menggunakan kembali node yang mendasar, sehingga versi baru dapat dibangun kembali dengan cepat dan dengan sedikit memori tambahan.

Pohon sintaksis secara harfiah adalah struktur data pohon, di mana elemen struktural non-terminal membawahi elemen lainnya. Setiap pohon sintaks terdiri dari simpul, token, dan trivia.

Simpul sintaksis

Simpul sintaks adalah salah satu elemen utama pohon sintaksis. Node ini mewakili konstruksi sintaktik seperti deklarasi, pernyataan, klausa, dan ekspresi. Setiap kategori simpul sintaksis diwakili oleh kelas terpisah yang berasal dari Microsoft.CodeAnalysis.SyntaxNode. Set kelas node tidak dapat diperluas.

Semua simpul sintaks adalah simpul non-terminal di pohon sintaks, yang berarti mereka selalu memiliki anak simpul dan token lain sebagai elemen turunan. Sebagai anak dari simpul lain, setiap simpul memiliki simpul induk yang dapat diakses melalui SyntaxNode.Parent properti . Karena simpul dan pohon tidak dapat diubah, induk simpul tidak pernah berubah. Akar pohon tidak memiliki induk.

Setiap simpul memiliki SyntaxNode.ChildNodes() metode , yang mengembalikan daftar simpul anak dalam urutan berurutan berdasarkan posisinya dalam teks sumber. Daftar ini tidak berisi token. Setiap simpul juga memiliki metode untuk memeriksa turunan, seperti DescendantNodes, DescendantTokens, atau DescendantTrivia - yang mewakili daftar semua simpul, token, atau trivia yang ada di subtree yang berakar oleh simpul tersebut.

Selain itu, setiap subkelas simpul sintaks mengekspos semua turunan yang sama melalui properti yang diketik dengan kuat. Misalnya, kelas node BinaryExpressionSyntax memiliki tiga properti tambahan khusus untuk operator biner: Left, OperatorToken, dan Right. Jenis Left dan Right adalah ExpressionSyntax, dan jenisnya OperatorToken adalah SyntaxToken.

Beberapa simpul sintaksis memiliki anak opsional. Misalnya, sebuah IfStatementSyntax memiliki ElseClauseSyntax yang bersifat opsional. Jika anak tidak ada, properti mengembalikan null.

Token sintaksis

Token sintaks adalah terminal tata bahasa, yang mewakili fragmen sintik terkecil dari kode. Mereka tidak pernah menjadi induk dari simpul atau elemen lainnya. Token sintaksis terdiri dari kata kunci, pengidentifikasi, literal, dan tanda baca.

Untuk tujuan efisiensi, tipe SyntaxToken adalah tipe nilai CLR. Oleh karena itu, tidak seperti simpul sintaksis, hanya ada satu struktur untuk semua jenis token dengan campuran properti yang memiliki arti tergantung pada jenis token yang sedang diwakili.

Misalnya, token literal bilangan bulat mewakili nilai numerik. Selain rentang token dari teks sumber mentah, token harfiah memiliki properti Value yang menunjukkan nilai tepat bilangan bulat yang telah didekodekan. Properti ini diketik sebagai Object karena bisa menjadi salah satu dari banyak tipe primitif.

Properti ValueText memberi tahu Anda informasi yang sama dengan Value properti ; namun properti ini selalu ditik sebagai String. Pengidentifikasi dalam teks sumber C# dapat menyertakan karakter escape Unicode, namun sintaks urutan escape itu sendiri tidak dianggap sebagai bagian dari nama pengidentifikasi. Jadi meskipun teks mentah yang dicakup oleh token memang menyertakan urutan escape, properti ValueText tidak. Sebaliknya, ini termasuk karakter Unicode yang diidentifikasi oleh escape. Misalnya, jika teks sumber berisi pengidentifikasi yang ditulis sebagai \u03C0, maka ValueText properti untuk token ini akan mengembalikan π.

Trivia sintaksis

Trivia sintaks mewakili bagian-bagian teks sumber yang sebagian besar tidak signifikan untuk pemahaman normal tentang kode, seperti spasi kosong, komentar, dan arahan pra-prosesor. Seperti token sintaks, trivia adalah jenis nilai. Jenis tunggal Microsoft.CodeAnalysis.SyntaxTrivia digunakan untuk menggambarkan semua jenis trivia.

Karena trivia bukan bagian dari sintaks bahasa normal dan dapat muncul di mana saja di antara dua token apa pun, mereka tidak disertakan dalam pohon sintaksis sebagai anak dari simpul. Namun, karena penting saat menerapkan fitur seperti pemfaktoran ulang dan untuk mempertahankan keakuratan penuh dengan teks sumber, fitur tersebut memang ada sebagai bagian dari pohon sintaksis.

Anda dapat mengakses trivia dengan memeriksa koleksi SyntaxToken.LeadingTrivia atau SyntaxToken.TrailingTrivia yang dimiliki token. Ketika teks sumber diurai, urutan trivia dikaitkan dengan token. Secara umum, token memiliki informasi tambahan setelahnya pada baris yang sama hingga token berikutnya. Setiap trivia setelah baris tersebut dikaitkan dengan token berikut. Token pertama dalam file sumber mendapatkan semua trivia awal, dan urutan terakhir trivia dalam file ditempelkan ke token akhir dari file, yang sebaliknya memiliki lebar nol.

Tidak seperti simpul sintaks dan token, trivia sintaksis tidak memiliki orang tua. Namun, karena mereka adalah bagian dari pohon dan masing-masing dikaitkan dengan satu token, Anda dapat mengakses token yang terkait dengannya menggunakan SyntaxTrivia.Token properti .

Rentang

Setiap simpul, token, atau trivia mengetahui posisinya dalam teks sumber dan jumlah karakter yang terdiri darinya. Posisi teks direpresentasikan sebagai bilangan bulat 32-bit, yang merupakan indeks berbasis char nol. Objek TextSpan adalah posisi awal dan jumlah karakter, keduanya direpresentasikan sebagai bilangan bulat. Jika TextSpan memiliki panjang nol, itu mengacu pada lokasi antara dua karakter.

Setiap simpul memiliki dua TextSpan properti: Span dan FullSpan.

Properti Span adalah rentang teks dari awal token pertama dalam subtree suatu simpul hingga akhir token terakhir. Rentang ini tidak termasuk trivia terkemuka atau berikutnya.

Properti FullSpan adalah rentang teks yang mencakup rentang normal simpul, ditambah rentang trivia yang mendahului atau mengikuti.

Contohnya:

      if (x > 3)
      {
||        // this is bad
          |throw new Exception("Not right.");|  // better exception?||
      }

Simpul pernyataan di dalam blok memiliki rentang yang ditunjukkan oleh bilah vertikal tunggal (|). Ini mencakup karakter throw new Exception("Not right.");. Rentang penuh ditunjukkan oleh bilah vertikal ganda (||). Ini mencakup karakter yang sama seperti rentang dan karakter yang terkait dengan informasi tambahan di bagian depan dan belakang.

Jenis

Setiap simpul, token, atau trivia memiliki properti SyntaxNode.RawKind, bertipe System.Int32, yang mengidentifikasi elemen sintaks yang tepat yang ditunjukkan. Nilai ini dapat dikonversi ke enumerasi khusus bahasa. Setiap bahasa, C# atau Visual Basic, memiliki enumerasi tunggal SyntaxKind (Microsoft.CodeAnalysis.CSharp.SyntaxKind dan Microsoft.CodeAnalysis.VisualBasic.SyntaxKind, masing-masing) yang mencantumkan semua kemungkinan node, token, dan elemen trivia dalam tata bahasa. Konversi ini dapat dilakukan secara otomatis dengan mengakses metode ekstensi CSharpExtensions.Kind atau VisualBasicExtensions.Kind.

Properti RawKind ini memungkinkan kemudahan disambiguasi jenis node sintaks yang berbagi kelas node yang sama. Untuk token dan trivia, properti ini adalah satu-satunya cara untuk membedakan satu jenis elemen dari yang lain.

Misalnya, satu BinaryExpressionSyntax kelas memiliki Left, OperatorToken, dan Right sebagai anak- anak. Properti Kind membedakan apakah itu adalah AddExpression, , SubtractExpressionatau MultiplyExpression jenis simpul sintaksis.

Petunjuk / Saran

Disarankan untuk memeriksa jenis menggunakan IsKind metode ekstensi (untuk C#) atau IsKind (untuk VB).

Kesalahan

Bahkan ketika teks sumber berisi kesalahan sintaksis, pohon sintaksis lengkap yang dapat diputar balik sepenuhnya ke sumber ditampilkan. Ketika pengurai menemukan kode yang tidak sesuai dengan sintaks bahasa yang ditentukan, ia menggunakan salah satu dari dua teknik untuk membuat pohon sintaks:

  • Jika perancang sintaks mengharapkan jenis token tertentu tetapi tidak menemukannya, ia mungkin akan menambahkan token yang hilang ke dalam pohon sintaks pada lokasi yang diharapkan. Token yang hilang mewakili token aktual yang diharapkan, tetapi memiliki rentang kosong, dan propertinya SyntaxNode.IsMissing mengembalikan true.

  • Pengurai mungkin melewati token hingga menemukan satu yang memungkinkan untuk melanjutkan penguraian. Dalam kasus ini, token yang dilewati akan dilampirkan sebagai nodus trivia dengan jenis SkippedTokensTrivia.