Bagikan melalui


Bekerja dengan sintaks

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

  • Untuk mengizinkan alat - seperti IDE, add-in, alat analisis kode, dan refaktor - untuk melihat dan memproses struktur sintaks kode sumber dalam proyek pengguna.
  • Untuk mengaktifkan alat - seperti refaktor 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 sintaks

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

Catatan

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:

  • Pohon sintaks menyimpan semua sumber informasi dengan fidelitas penuh. Fidelitas penuh berarti bahwa pohon sintaks berisi setiap bagian informasi yang ditemukan dalam teks sumber, setiap konstruksi tata bahasa, setiap token leksikal, dan segala sesuatu di antaranya, termasuk spasi, komentar, dan arahan praprosesor. Misalnya, setiap literal yang disebutkan dalam sumber diwakili persis seperti yang diketik. Pohon sintaks juga menangkap kesalahan dalam kode sumber ketika program tidak lengkap atau salah format dengan mewakili token yang dilewati atau hilang.
  • Pohon sintaks dapat menghasilkan teks persis seperti yang diurai. Dari node sintaks mana pun, Anda bisa mendapatkan representasi teks dari subpohon yang di-root pada node tersebut. Kemampuan ini berarti bahwa pohon sintaks dapat digunakan sebagai cara untuk membangun dan mengedit teks sumber. Dengan membuat pohon, Anda secara implisit telah membuat teks yang sama, dan dengan membuat pohon baru dari perubahan pada pohon yang ada, Anda telah mengedit teks secara efektif.
  • Pohon tidak dapat diubah dan bersifat aman untuk utas. Setelah diperoleh, pohon akan menjadi snapshot dari status kode saat ini dan tidak pernah berubah. Hal ini memungkinkan beberapa pengguna untuk berinteraksi dengan pohon sintaks yang sama pada waktu 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 sintaks dengan membuat snapshot tambahan dari pohon. Pohon-pohon efisien dalam cara menggunakan kembali node yang mendasarinya, sehingga versi baru dapat dibangun kembali dengan cepat dan dengan sedikit memori tambahan.

Pohon sintaks secara harfiah adalah struktur data pohon, dengan elemen struktural non-terminal menjadi induk elemen lainnya. Setiap pohon sintaks terdiri dari node, token, dan trivia.

Node sintaks

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

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

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

Selain itu, setiap subkelas node 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 jenis OperatorToken adalah SyntaxToken.

Beberapa node sintaks memiliki turunan opsional. Misalnya, IfStatementSyntax memiliki ElseClauseSyntax opsional. Jika turunan tidak ada, properti akan menampilkan null.

Token sintaks

Token sintaks adalah terminal tata bahasa, yang mewakili fragmen sintaks terkecil dari kode. Token tidak pernah menjadi induk dari node atau token lain. Token sintaks terdiri dari kata kunci, pengidentifikasi, harfiah, dan tanda baca.

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

Misalnya, token harfiah bilangan bulat mewakili nilai numerik. Selain teks sumber mentah yang dibentangkan token, token harfiah memiliki properti Value yang memberi tahu Anda nilai bilangan bulat yang didekodekan dengan tepat. Properti ini diketik sebagai Object karena mungkin merupakan salah satu dari banyak jenis primitif.

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

Sintaks trivia

Sintaks trivia mewakili bagian dari teks sumber yang sebagian besar tidak signifikan untuk pemahaman normal kode, seperti white space, komentar, dan arahan praprosesor. Seperti token sintaks, trivia adalah jenis nilai. Jenis Microsoft.CodeAnalysis.SyntaxTrivia tunggal digunakan untuk menjelaskan semua jenis trivia.

Karena trivia bukan bagian dari sintaks bahasa normal dan dapat muncul di mana saja di antara dua token mana pun, trivia tidak termasuk dalam pohon sintaks sebagai turunan dari sebuah node. Namun, karena trivia penting saat mengimplementasikan fitur seperti refaktor dan untuk menjaga fidelitas penuh dengan teks sumber, trivia memang ada sebagai bagian dari pohon sintaks.

Anda dapat mengakses trivia dengan memeriksa token SyntaxToken.LeadingTrivia atau SyntaxToken.TrailingTrivia koleksi. Ketika teks sumber diuraikan, urutan trivia dikaitkan dengan token. Secara umum, token memiliki trivia apa pun setelahnya pada baris yang sama hingga token berikutnya. Trivia apa pun 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 file, yang jika tidak, akan memiliki lebar nol.

Tidak seperti node sintaks dan token, sintaks trivia tidak memiliki induk. Namun, karena sintaks adalah bagian dari pohon dan masing-masing terkait dengan satu token, Anda dapat mengakses token yang dikaitkan dengan menggunakan properti SyntaxTrivia.Token.

Rentang

Setiap node, 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 char berbasis nol. Objek TextSpan adalah posisi awal dan jumlah karakter, keduanya direpresentasikan sebagai bilangan bulat. Jika TextSpan memiliki panjang nol, objek akan merujuk ke lokasi di antara dua karakter.

Setiap node memiliki dua properti TextSpan: Span dan FullSpan.

Properti Span adalah rentang teks dari awal token pertama di subpohon node hingga akhir token terakhir. Rentang ini tidak termasuk trivia awal atau akhir.

Properti FullSpan adalah rentang teks yang menyertakan rentang normal node, ditambah rentang trivia awal atau akhir.

Misalnya:

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

Node pernyataan di dalam blok memiliki rentang yang ditunjukkan oleh bilah vertikal tunggal (|). Rentang ini mencakup karakter throw new Exception("Not right.");. Rentang penuh ditunjukkan oleh bilah vertikal ganda (||). Rentang ini mencakup karakter yang sama dengan rentang dan karakter yang terkait dengan trivia awal dan akhir.

Jenis

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

Properti RawKind memungkinkan disambiguasi yang mudah dari 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 kelas BinaryExpressionSyntax memiliki Left, OperatorToken, dan Right sebagai turunan. Properti Kind membedakan apakah itu jenis node sintaks AddExpression, SubtractExpression, atau MultiplyExpression.

Tip

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

Kesalahan

Bahkan ketika teks sumber berisi kesalahan sintaks, pohon sintaks lengkap yang dapat dipindah-pindahkan ke sumbernya akan diekspos. Ketika pengurai menemukan kode yang tidak sesuai dengan sintaks bahasa yang ditentukan, pengurai menggunakan salah satu dari dua teknik untuk membuat pohon sintaks:

  • Jika pengurai mengharapkan jenis token tertentu tetapi tidak menemukannya, pengurai mungkin memasukkan token yang hilang ke dalam pohon sintaks di lokasi yang diharapkan oleh token tersebut. Token yang hilang mewakili token sebenarnya yang diharapkan, tetapi memiliki rentang kosong, dan properti SyntaxNode.IsMissing-nya menampilkan true.

  • Pengurai dapat melewati token hingga menemukan token tempatnya dapat melanjutkan penguraian. Dalam hal ini, token yang dilewati dilampirkan sebagai node trivia dengan jenis SkippedTokensTrivia.