Menulis Kompiler Kompiler - Wawasan Penggunaan dan Fitur

10

Ini adalah bagian dari serangkaian pertanyaan yang berfokus pada proyek saudara ke Proyek Abstraksi, yang bertujuan untuk abstrak konsep yang digunakan dalam desain bahasa dalam bentuk kerangka kerja. Proyek saudara disebut OILexer, yang bertujuan untuk membangun parser dari file tata bahasa, tanpa menggunakan injeksi kode pada pertandingan.

Beberapa halaman lain yang terkait dengan pertanyaan ini, terkait dengan pengetikan struktural, dapat dilihat di sini , dan kemudahan penggunaannya, ditemukan di sini . Meta-topik yang terkait dengan penyelidikan tentang kerangka kerja dan tempat yang tepat untuk memposting dapat ditemukan di sini .

Saya sampai pada titik di mana saya akan mulai mengekstraksi pohon parse dari tata bahasa yang diberikan, diikuti oleh parser Keturunan Rekursif yang menggunakan DFA untuk membedakan jalur maju (mirip dengan LL ANTLR 4 (*)), jadi saya kupikir aku akan membukanya untuk mendapatkan wawasan.

Dalam kompiler parser, fitur apa saja yang ideal?

Sejauh ini di sini adalah gambaran singkat tentang apa yang diterapkan:

  1. Templat
  2. Lihat ke depan prediksi, mengetahui apa yang valid pada titik tertentu.
  3. Peraturan 'Deliteralization' mengambil literal dalam aturan dan memutuskan dari mana token mereka berasal.
  4. Automata Nondeterministic
  5. Deterministic Automata
  6. Mesin status leksikal sederhana untuk pengenalan token
  7. Metode otomatisasi token:
    • Pindai - berguna untuk komentar: Komentar: = "/ *" Pindai ("* /");
    • Kurangi - Berguna untuk Pengidentifikasi: Pengidentifikasi: = Kurangi (IdentifierBody, Kata Kunci);
      • Pastikan pengidentifikasi tidak menerima kata kunci.
    • Encode - Mengkode otomatisasi sebagai hitungan X seri transisi basis N.
      • UnicodeEscape: = "\\ u" BaseEncode (IdentifierCharNoEscape, 16, 4);
        • Membuat pelarian unicode dalam heksadesimal, dengan hex 4-transisi. Perbedaan antara ini dan: [0-9A-Fa-f] {4} adalah otomatisasi yang dihasilkan dengan Encode membatasi set nilai heksadesimal yang diizinkan ke ruang lingkup IdentifierCharNoEscape. Jadi, jika Anda memberikannya \ u005c, versi enkode tidak akan menerima nilai. Hal-hal seperti ini memiliki peringatan serius: Gunakan hemat. Otomatisasi yang dihasilkan bisa sangat kompleks.

Apa yang tidak diimplementasikan adalah generasi CST, saya perlu menyesuaikan otomatisasi Deterministic untuk membawa konteks yang tepat untuk membuatnya bekerja.

Bagi siapa pun yang tertarik, saya telah mengunggah sebuah cetakan cantik dari bentuk asli proyek T * y♯ . Setiap file harus ditautkan ke setiap file lainnya, saya mulai menautkan dalam aturan individu untuk mengikuti mereka, tetapi itu akan memakan waktu terlalu lama (akan lebih mudah untuk diotomatisasi!)

Jika lebih banyak konteks diperlukan, silakan posting sesuai dengan itu.

Sunting 5-14-2013 : Saya telah menulis kode untuk membuat grafik GraphViz untuk mesin negara dalam bahasa yang diberikan. Berikut ini adalah grafik GraphViz dari AssemblyPart . Anggota yang ditautkan dalam uraian bahasa harus memiliki ruleename.txt di folder relatifnya dengan digraf untuk aturan itu. Beberapa uraian bahasa telah berubah sejak saya memposting contoh, ini karena menyederhanakan hal-hal tentang tata bahasa. Berikut ini adalah gambar graphviz yang menarik .

Alexander Morou
sumber
8
Teks dinding. Jangan salah paham, saya menghargai masalah yang dijelaskan dengan seksama. Dalam hal ini hanya sedikit terlalu bertele-tele. Dari apa yang saya kumpulkan, Anda bertanya fitur apa yang harus dimasukkan dalam tata bahasa tata bahasa atau bagaimana membuatnya tanpa mulai dari awal? Harap edit untuk menjawab pertanyaan berikut (Anda tidak perlu menulis ulang, cukup tambahkan sampai akhir dalam ringkasan): Apa masalah Anda? Kendala apa yang Anda terikat dalam solusi yang mungkin untuk masalah Anda (harus cepat, harus LL *, dll.)?
Neil
1
Saya meminta wawasan tentang set fitur. Fokusnya adalah pada kemudahan penggunaan. Kesulitannya terletak pada mendapatkan seseorang yang tidak mengetahui proyek, wawasan tentang proyek sehingga mereka diberitahu tentang fokusnya. Saya tidak meminta 'bagaimana melakukan', saya bertanya terkait dengan kegunaan. Saran tentang cara memangkas pertanyaan dihargai.
Allen Clark Copeland Jr
1
Bagi saya, tidak jelas apa proyek itu. Sebagai contoh, sejak zaman yacc, kami telah melihat banyak generator parser. Apa yang berbeda di OILexer Anda? Apa yang baru?
Ingo
1
Tujuan dari proyek ini adalah untuk menyederhanakan generasi parser. Mirip ya, dengan YACC / Bison dan FLEX / LEX. Perbedaan utama adalah untuk menghindari kompleksitas yang terlibat dalam program-program tersebut. Menjaga segala sesuatunya sederhana dan to the point adalah tujuan utama. Inilah sebabnya saya membuat format yang tidak memiliki pembagian yang aneh, tetapi tujuannya adalah untuk membuatnya mirip dengan pemrograman biasa: hanya khusus untuk pengembangan bahasa. Token didefinisikan menggunakan ': =' setelah nama mereka, aturan didefinisikan menggunakan :: = setelah nama mereka. Template menggunakan '<' dan '>' untuk argumen mereka diikuti oleh ":: =" karena mereka berbagi sintaksis aturan.
Allen Clark Copeland Jr
3
Fokus neraka pada penguraian ini tampaknya salah tempat; ini adalah masalah yang terpecahkan dengan baik, dan hampir tidak merusak apa yang Anda butuhkan untuk memproses bahasa pemrograman. Google untuk esai saya tentang "kehidupan setelah penguraian".
Ira Baxter

Jawaban:

5

Ini adalah pertanyaan yang sangat bagus.

Saya telah mengerjakan banyak parsing baru-baru ini, dan IMHO beberapa fitur utama adalah:

  • API terprogram - sehingga dapat digunakan dari dalam bahasa pemrograman, idealnya hanya dengan mengimpor perpustakaan. Ini dapat memiliki antarmuka seperti GUI atau BNF juga, tetapi yang terprogram adalah kuncinya, karena Anda dapat menggunakan kembali tooling, IDE, analisis statis, pengujian, fasilitas abstraksi bahasa, keakraban programmer, generator dokumentasi, proses pembuatan, dll. Plus, Anda bisa bermain secara interaktif dengan parser kecil, yang secara dramatis mengurangi kurva belajar. Alasan ini menempatkannya di bagian atas daftar "fitur penting" saya.

  • pelaporan kesalahan, seperti yang disebutkan @guysherman. Ketika kesalahan ditemukan, saya ingin tahu di mana kesalahan itu dan apa yang terjadi ketika itu terjadi. Sayangnya, saya belum dapat menemukan sumber daya yang baik untuk menjelaskan cara menghasilkan kesalahan yang layak saat backtracking dimainkan. (Meskipun perhatikan @ Sk-logika komentar di bawah ini).

  • hasil parsial. Ketika parsing gagal, saya ingin dapat melihat apa yang berhasil diurai dari bagian input yang sebelum lokasi kesalahan.

  • abstraksi. Anda tidak pernah dapat membangun fungsi yang cukup, dan pengguna akan selalu membutuhkan lebih banyak, jadi mencoba mencari tahu semua fungsi yang mungkin ada di muka akan menemui kegagalan. Apakah ini yang Anda maksud dengan templat?

  • Saya setuju dengan # 2 Anda (prediksi prediksi). Saya pikir ini membantu untuk menghasilkan laporan kesalahan yang baik. Apakah ini berguna untuk hal lain?

  • dukungan untuk membangun pohon parse saat parsing terjadi, mungkin:

    • pohon sintaksis konkret, di mana struktur pohon terkait langsung dengan tata bahasa dan termasuk informasi tata letak untuk pelaporan kesalahan fase selanjutnya. Dalam hal ini, klien tidak harus melakukan apa pun untuk mendapatkan struktur pohon yang tepat - itu harus bergantung langsung pada tata bahasa.
    • pohon sintaksis abstrak. Dalam hal ini, pengguna dapat bermain-main dengan semua dan semua pohon parse
  • semacam penebangan. Saya tidak yakin tentang yang ini; mungkin untuk menunjukkan jejak aturan yang telah diuraikan parser, atau untuk melacak token sampah seperti spasi putih atau komentar, dalam kasus (misalnya) Anda ingin menggunakan token untuk menghasilkan dokumentasi HTML.

  • kemampuan untuk berurusan dengan bahasa yang peka konteks. Tidak yakin seberapa penting yang satu ini, baik - dalam praktiknya, tampaknya lebih bersih untuk mengurai superset dari bahasa dengan tata bahasa bebas konteks, kemudian untuk memeriksa kendala peka konteks dalam tambahan selanjutnya.

  • pesan kesalahan khusus, sehingga saya dapat mengatur laporan kesalahan dalam situasi tertentu dan mungkin lebih cepat memahami dan memperbaiki masalah.

Di sisi lain, saya tidak menemukan koreksi kesalahan sangat penting - meskipun saya tidak mengetahui perkembangan terkini. Masalah yang saya perhatikan adalah bahwa koreksi potensial yang disediakan oleh peralatan tersebut adalah: 1) terlalu banyak, dan 2) tidak sesuai dengan kesalahan yang sebenarnya dibuat, dan karena itu tidak terlalu membantu. Semoga situasi ini akan membaik (atau mungkin sudah melakukannya).


sumber
Saya telah mengedit badan pertanyaan untuk memasukkan tautan ke PrecedenceHelper dalam butir yang bertuliskan 'Templat'. Ini memungkinkan tupel array parameter, jadi jika Anda memiliki empat parameter, setiap parameter array, template harus digunakan dalam set argumen empat.
Allen Clark Copeland Jr
Alasan utama Anda membuat CST adalah tata letak keseluruhan file yang diuraikan. Jika Anda ingin mencetak pracetak dokumen, taruhan terbaik Anda adalah menggunakan CST karena AST sebagai namanya menyiratkan kurangnya informasi untuk menangani spasi aneh yang CST akan tangkap. Mengubah CST biasanya cukup mudah jika CST yang bagus.
Allen Clark Copeland Jr
Saya telah mengedit lagi pertanyaan tentang topik fungsi bawaan yang tersedia untuk digunakan.
Allen Clark Copeland Jr
Saya pikir saya tidak melakukan pekerjaan yang bagus dengan mengutarakan pendapat saya tentang templat / fungsi: Maksud saya, karena Anda tidak akan pernah punya cukup, suatu sistem tidak boleh mencoba untuk mengetahuinya sebelumnya: pengguna harus dapat membuat sendiri.
1
Saya menemukan satu pendekatan yang sangat berguna untuk pelaporan kesalahan dengan backtracking tak terbatas (Packrat): setiap aturan produksi ditandai dengan pesan kesalahan (kata sebagai "bla-bla-bla diharapkan"), dan jika gagal pesan seperti itu disimpan dalam aliran dengan cara yang sama sebagai token memoised. Jika semua kegagalan tidak dapat dipulihkan (parsing dihentikan sebelum mencapai akhir aliran), pesan kesalahan paling kanan (atau kumpulan pesan tersebut) adalah yang paling tepat. Itu hal termudah untuk dilakukan, tentu saja ada cara untuk memperbaikinya lebih lanjut dengan lebih banyak anotasi.
SK-logic
5

Saya tidak berpengalaman dalam desain bahasa, tetapi saya pernah mencoba menulis parser sekali, ketika saya sedang membuat dan IDE untuk mesin permainan.

Sesuatu yang penting bagi pengguna akhir Anda, menurut pendapat saya, adalah pesan kesalahan yang masuk akal. Saya tahu, bukan titik yang sangat mengguncang bumi, tetapi mengikutinya ke belakang, salah satu implikasi utama dari hal ini adalah Anda harus dapat menghindari hal-hal yang salah. Dari mana asal positif palsu? Mereka datang dari pengurai yang jatuh pada kesalahan pertama dan tidak pernah benar-benar pulih. C / C ++ terkenal karena hal ini (walaupun kompiler yang lebih baru sedikit lebih pintar).

Jadi, apa yang Anda butuhkan? Saya pikir daripada hanya tahu apa yang valid / tidak valid pada suatu titik, Anda perlu tahu bagaimana mengambil apa yang tidak valid dan membuat perubahan minimal untuk membuatnya valid - sehingga Anda dapat melanjutkan penguraian tanpa menghasilkan kesalahan palsu terkait untuk keturunan rekursif Anda menjadi bingung. Mampu membangun parser yang dapat menghasilkan informasi ini tidak hanya memberi Anda parser yang sangat kuat, tetapi membuka beberapa fitur fantastis untuk perangkat lunak yang mengkonsumsi parser.

Saya menyadari bahwa saya mungkin menyarankan sesuatu yang benar-benar sulit, atau jelas sekali, maaf jika ini masalahnya. Jika ini bukan hal yang Anda cari, dengan senang hati saya akan menghapus jawaban saya.

jagoan
sumber
Ini adalah salah satu hal yang saya rencanakan untuk dilakukan. Untuk membantu dengan pengetahuan saya tentang domain, seorang teman saya menyarankan untuk menulis parser yang sebenarnya dengan tangan agar bisa menguasainya secara otomatis. Satu hal yang saya sadari dengan cepat: parser itu rumit dan ada hal-hal yang kami lakukan dengan tangan yang menyederhanakan prosesnya. Aturan dan Template berbagi sintaks yang sama; Namun, ada elemen bahasa yang berlaku di Templat tetapi tidak aturan, ada keadaan internal yang menangani tugas ini. Yang membawa ide ke pikiran: aturan harus dapat menentukan alat bantu jalan untuk membuat berbagi sub-aturan lebih mudah.
Allen Clark Copeland Jr
... ini harus cukup sederhana untuk dibawa ke dalam otomatisasi, tetapi akan membutuhkan otomatisasi untuk memiliki kondisi berbasis negara. Saya akan mengerjakan beberapa ini dan kembali kepada Anda. ANTLR menggunakan otomasi keadaan terbatas untuk menangani siklus katakanlah: "T" *, di mana saya akan menggunakannya untuk menangani sebagian besar proses parse karena pengurangan harus lebih bersih sebagai keadaan ketika ada 800+ variasi dalam aturan (ini akan menggembung secepat kode spaghetti dalam bentuk standar if / else.)
Allen Clark Copeland Jr
0

Tata bahasanya tidak boleh memiliki batasan seperti "jangan tinggalkan aturan rekursif". Ini konyol bahwa alat banyak digunakan saat ini memiliki ini dan hanya dapat memahami mengisap tata bahasa LL - hampir 50 tahun setelah yacc melakukannya dengan benar.

Contoh untuk rekursi kanan (menggunakan sintaksis yacc):

list: 
      elem                  { $$ = singleton($1); }
    | elem ',' list         { $$ = cons($1, $2);  }
    ;

Contoh untuk rekursi kiri (menggunakan yacc synatx):

funapp:
    term                    { $$ = $1; }
    | funapp term           { $$ = application($1, $2); }
    ;

Sekarang, ini mungkin "refactored" untuk sesuatu yang lain, tetapi dalam kedua kasus, jenis rekursi khusus adalah cara yang "benar" untuk menulis ini, karena (dalam bahasa contoh) daftar adalah rekursif yang tepat sementara aplikasi fungsi dibiarkan rekursif .

Seseorang dapat mengharapkan dari alat-alat canggih bahwa mereka mendukung cara alami untuk menulis barang, alih-alih mengharuskan seseorang untuk "refactor" segala sesuatu menjadi kiri / kanan rekursif bentuk alat memaksakan ke satu.

Ingo
sumber
Ya, senang tidak memiliki batasan sewenang-wenang seperti itu. Namun, apa contoh rekursi kiri yang tidak dapat diganti dengan operator pengulangan (seperti regex *atau +quantifiers)? Saya dengan bebas mengakui bahwa pengetahuan saya di bidang ini terbatas, tetapi saya tidak pernah mengalami penggunaan rekursi kiri yang tidak dapat di refactored menjadi pengulangan. Dan saya menemukan versi pengulangan lebih jelas juga (walaupun itu hanya preferensi pribadi).
1
@MattFenwick Lihat hasil edit saya. Perhatikan bagaimana sintaks mengarahkan langsung ke tindakan semantik dan alami (misalnya) untuk membuat pohon sintaks. Sementara dengan pengulangan, (yang tidak tersedia dalam yacc, btw), saya kira Anda sering perlu memeriksa apakah Anda punya daftar kosong, singleton, dll.
Ingo
Terima kasih atas tanggapannya. Saya pikir saya mengerti lebih baik sekarang - saya lebih suka menulis contoh-contoh itu sebagai list = sepBy1(',', elem)dan funapp = term{+}(dan tentu saja sepBy1dan +akan diimplementasikan dalam hal rekursi kiri / kanan, dan menghasilkan pohon sintaksis standar). Jadi bukan karena saya pikir rekursi kiri dan kanan itu buruk, hanya saja saya merasa mereka tingkat rendah dan ingin menggunakan abstraksi tingkat yang lebih tinggi di mana mungkin untuk membuat segalanya menjadi lebih jelas. Terima kasih lagi!
1
Terima kasih @MattFenwick. Tapi mungkin itu masalah selera. Bagi saya, rekursi (setidaknya dalam konteks bahasa, yang semuanya secara inheren bersifat rekursif atau sama sekali tidak menarik) adalah cara yang lebih alami untuk memikirkannya. Juga, pohon adalah struktur data rekursif, jadi saya melihat tidak perlu kembali ke iterasi untuk mensimulasikan rekursi. Tetapi, tentu saja, preferensi berbeda.
Ingo