Apakah mungkin untuk membuat penerjemah "bootstrapped" independen dari penerjemah asli?

21

Menurut Wikipedia, istilah "bootstrap" dalam konteks penulisan kompiler artinya :

Dalam ilmu komputer, bootstrap adalah proses penulisan kompiler (atau assembler) dalam bahasa pemrograman sumber yang ingin dikompilasi. Menerapkan teknik ini mengarah ke kompiler self-hosting.

Dan saya bisa mengerti bagaimana cara kerjanya. Namun, ceritanya agak berbeda bagi penerjemah. Sekarang, tentu saja, dimungkinkan untuk menulis juru bahasa hosting sendiri. Bukan itu yang saya tanyakan. Yang sebenarnya saya tanyakan adalah: Apakah mungkin membuat penerjemah yang di-hosting-sendiri tidak tergantung dari penerjemah pertama yang asli . Untuk menjelaskan apa yang saya maksud, pertimbangkan contoh ini:

Anda menulis versi interpreter pertama Anda dalam bahasa X , dan penafsir adalah untuk bahasa baru Anda membuat, disebut Y . Anda pertama kali menggunakan kompiler bahasa X untuk membuat executable. Anda sekarang dapat menafsirkan file yang ditulis dalam bahasa baru Anda Y menggunakan penerjemah yang ditulis dalam bahasa X .

Sekarang, sejauh yang saya mengerti, untuk dapat "bootstrap" interpreter Anda menulis dalam bahasa X , Anda akan perlu untuk menulis ulang interpreter dalam bahasa Y . Tapi di sini adalah menangkap: bahkan jika Anda menulis ulang seluruh juru bahasa dalam bahasa Y , Anda masih akan membutuhkan asli interpreter Anda menulis dalam bahasa X . Karena untuk menjalankan penerjemah dalam bahasa Y , Anda harus menginterpretasikan file sumber. Tetapi apa sebenarnya yang akan menafsirkan file sumber? Yah, tentu saja itu bukan apa-apa, jadi Anda terpaksa masih menggunakan penerjemah pertama.

Tidak peduli berapa banyak penerjemah baru yang Anda tulis dalam bahasa Y , Anda selalu harus menggunakan penerjemah pertama yang ditulis dalam X untuk menafsirkan penerjemah berikutnya. Ini tampaknya menjadi masalah hanya karena sifat penafsir.

Namun , di sisi lain, artikel Wikipedia tentang penerjemah ini sebenarnya berbicara tentang penerjemah mandiri . Berikut adalah kutipan kecil yang relevan:

Interpreter mandiri adalah interpreter bahasa pemrograman yang ditulis dalam bahasa pemrograman yang dapat menafsirkan dirinya sendiri; contohnya adalah juru bahasa BASIC yang ditulis dalam bahasa BASIC. Interpreter mandiri terkait dengan kompiler hosting mandiri.

Jika tidak ada kompiler untuk bahasa yang akan ditafsirkan, membuat penerjemah sendiri membutuhkan implementasi bahasa dalam bahasa host (yang mungkin merupakan bahasa pemrograman lain atau assembler). Dengan memiliki juru bahasa pertama seperti ini, sistem di-bootstrap dan versi-versi baru dari juru bahasa tersebut dapat dikembangkan dalam bahasa itu sendiri

Namun masih belum jelas bagi saya, bagaimana tepatnya hal ini dilakukan. Tampaknya apa pun yang terjadi, Anda selalu akan dipaksa untuk menggunakan versi pertama penerjemah Anda yang ditulis dalam bahasa host.

Sekarang artikel yang disebutkan di atas menghubungkan ke artikel lain di mana Wikipedia memberikan beberapa contoh penerjemah self-hosting yang seharusnya . Namun, setelah diperiksa lebih dekat, tampaknya bagian "penafsiran" utama dari banyak penafsir hosting sendiri (terutama beberapa yang lebih umum seperti PyPy atau Rubinius) sebenarnya ditulis dalam bahasa lain seperti C ++ atau C.

Jadi apa yang saya jelaskan di atas mungkin? Bisakah juru bahasa yang di-host-sendiri independen dari host aslinya? Jika demikian, bagaimana tepatnya hal ini dilakukan?

Christian Dean
sumber

Jawaban:

24

Jawaban singkatnya adalah: Anda benar dalam kecurigaan Anda, Anda selalu membutuhkan penerjemah lain yang ditulis dalam X atau kompiler dari Y ke beberapa bahasa lain yang sudah Anda miliki juru bahasanya. Interpreter mengeksekusi, compiler hanya menerjemahkan dari satu bahasa ke bahasa lain, di beberapa titik dalam sistem Anda, ada harus menjadi juru ... bahkan itu hanya CPU.

Tidak peduli berapa banyak penerjemah baru yang Anda tulis dalam bahasa Y , Anda selalu harus menggunakan penerjemah pertama yang ditulis dalam X untuk menafsirkan penerjemah berikutnya. Ini tampaknya menjadi masalah hanya karena sifat penafsir.

Benar. Apa yang Anda bisa lakukan adalah menulis compiler dari Y ke X (atau bahasa lain yang Anda memiliki seorang penerjemah), dan Anda bahkan dapat melakukan itu di Y . Kemudian Anda dapat menjalankan kompiler Y Anda yang ditulis dalam Y pada juru bahasa Y yang ditulis dalam X (atau pada juru bahasa Y yang ditulis dalam Y yang berjalan pada juru bahasa Y yang ditulis dalam X , atau pada juru bahasa Y yang ditulis dalam Y yang menjalankan juru bahasa Y yang ditulis dalam Y berlari di atas Ypenerjemah ditulis dalam X , atau ... ad infinitum) untuk mengkompilasi penerjemah Y Anda ditulis dalam Y ke X , sehingga Anda kemudian dapat menjalankannya pada penerjemah X. Dengan begitu, Anda telah menyingkirkan penerjemah Y Anda yang ditulis dalam X , tetapi sekarang Anda membutuhkan penerjemah X (kami tahu bahwa kami sudah memilikinya, karena, jika tidak, kami tidak dapat menjalankan penerjemah X yang ditulis dalam Y ), dan Anda harus menulis Y -to- X -compiler pertama.

Namun , di sisi lain, artikel Wikipedia tentang penerjemah sebenarnya berbicara tentang penerjemah mandiri. Berikut adalah kutipan kecil yang relevan:

Interpreter mandiri adalah interpreter bahasa pemrograman yang ditulis dalam bahasa pemrograman yang dapat menafsirkan dirinya sendiri; contohnya adalah juru bahasa BASIC yang ditulis dalam bahasa BASIC. Interpreter mandiri terkait dengan kompiler hosting mandiri.

Jika tidak ada kompiler untuk bahasa yang akan ditafsirkan, membuat penerjemah sendiri membutuhkan implementasi bahasa dalam bahasa host (yang mungkin merupakan bahasa pemrograman lain atau assembler). Dengan memiliki juru bahasa pertama seperti ini, sistem di-bootstrap dan versi-versi baru dari juru bahasa tersebut dapat dikembangkan dalam bahasa itu sendiri

Namun masih belum jelas bagi saya, bagaimana tepatnya hal ini dilakukan. Tampaknya apa pun yang terjadi, Anda selalu akan dipaksa untuk menggunakan versi pertama penerjemah Anda yang ditulis dalam bahasa host.

Benar. Perhatikan bahwa artikel Wikipedia secara eksplisit mengatakan bahwa Anda memerlukan implementasi kedua bahasa Anda, dan itu tidak mengatakan bahwa Anda dapat menyingkirkan yang pertama.

Sekarang artikel yang disebutkan di atas menghubungkan ke artikel lain di mana Wikipedia memberikan beberapa contoh penerjemah self-hosting yang seharusnya. Namun, setelah diperiksa lebih dekat, tampaknya bagian "penafsiran" utama dari banyak penafsir hosting sendiri (terutama beberapa yang lebih umum seperti PyPy atau Rubinius) sebenarnya ditulis dalam bahasa lain seperti C ++ atau C.

Sekali lagi, benar. Itu adalah contoh yang sangat buruk. Ambil Rubinius, misalnya. Ya, memang benar bahwa bagian Ruby dari Rubinius di-host-sendiri, tetapi itu adalah kompiler, bukan penerjemah: ia mengkompilasi kode sumber Ruby ke bytecode Rubinius. Bagian penerjemah OTOH tidak di-host-sendiri: ini mengartikan bytecode Rubinius, tetapi ditulis dalam C ++. Jadi, menyebut Rubinius sebagai "penerjemah yang di-hosting-sendiri" adalah salah: bagian yang di -hosting-sendiri bukan seorang juru bahasa , dan bagian juru bahasa tidak di -hosting-sendiri .

PyPy mirip, tetapi bahkan lebih salah: ia bahkan tidak ditulis dengan Python, ia ditulis dalam RPython, yang merupakan bahasa yang berbeda. Secara sintaksis mirip dengan Python, secara semantik merupakan "subset diperluas", tetapi sebenarnya adalah bahasa yang diketik secara statis kira-kira pada tingkat abstraksi yang sama dengan Java, dan implementasinya adalah kompiler dengan beberapa backend yang mengkompilasi kode sumber RPython ke C, ECMAScript kode sumber, kode byte CIL, bytecode JVM, atau kode sumber Python.

Jadi apa yang saya jelaskan di atas mungkin? Bisakah penerjemah tuan rumah mandiri dari tuan rumah aslinya? Jika demikian, bagaimana tepatnya hal ini dilakukan?

Tidak, tidak dengan sendirinya. Anda perlu menyimpan penerjemah asli atau menulis kompiler dan mengkompilasi penerjemah mandiri Anda.

Ada yang beberapa meta-melingkar VMS, seperti Klein (ditulis dalam Diri ) dan Maxine (ditulis di Jawa). Perhatikan, bagaimanapun, bahwa di sini definisi "meta-sirkular" belum berbeda: VM ini tidak ditulis dalam bahasa yang mereka jalankan: Klein mengeksekusi bytecode diri tetapi ditulis dalam Self, Maxine mengeksekusi bytecode JVM tetapi ditulis dalam Java. Namun, Diri / Java kode sumber VM benar-benar akan dikompilasi untuk Diri / JVM bytecode dan kemudian dieksekusi oleh VM, sehingga pada saat VM dijalankan, itu adalah dalam bahasa dijalankan. Fiuh.

Perhatikan juga bahwa ini berbeda dari VM seperti SqueakVM dan Jikes RVM . Jikes ditulis dalam Java, dan SqueakVM ditulis dalam bahasa Slang (suatu sintaksis yang diketik secara statik dan subtek dari Smalltalk kira-kira pada tingkat abstraksi yang sama dengan assembler tingkat tinggi), dan keduanya dikompilasi secara statis dengan kode asli sebelum dijalankan. Mereka tidak lari ke dalam diri mereka sendiri. Namun, Anda dapat menjalankannya di atas diri mereka sendiri (atau di atas Smalltalk VM / JVM lainnya). Tapi itu bukan "meta-melingkar" dalam pengertian ini.

Maxine dan Klein, OTOH lakukanlari di dalam diri mereka sendiri; mereka mengeksekusi bytecode mereka sendiri menggunakan implementasi mereka sendiri. Ini benar-benar melegakan hati! Ini memungkinkan beberapa peluang optimisasi yang keren, misalnya karena VM mengeksekusi dirinya sendiri bersama dengan program pengguna, ia dapat menyatukan panggilan dari program pengguna ke VM dan sebaliknya, misalnya panggilan ke pengumpul sampah atau pengalokasi memori dapat digabungkan ke dalam pengguna kode, dan panggilan balik reflektif dalam kode pengguna dapat dimasukkan ke dalam VM. Juga, semua trik optimisasi pintar yang dilakukan oleh VM modern, di mana mereka menonton program yang mengeksekusi dan mengoptimalkannya tergantung pada beban kerja dan data yang sebenarnya, VM dapat menerapkan trik-trik yang sama untuk dirinya sendiri ketika menjalankan program pengguna sementara program pengguna sedang mengeksekusi beban kerja spesifik. Dengan kata lain, VM sangat mengkhususkan diri untuk ituprogram tertentu berjalan yang beban kerja tertentu.

Namun, perhatikan saya mengitari penggunaan kata "interpreter" di atas, dan selalu menggunakan "mengeksekusi"? Nah, VMs itu tidak dibangun di sekitar interpreter, mereka dibangun di sekitar (JIT) kompiler. Ada penerjemah yang ditambahkan ke Maxine nanti, tetapi Anda selalu membutuhkan kompiler: Anda harus menjalankan VM sekali di atas VM lain (mis. Oracle HotSpot dalam kasus Maxine), sehingga VM dapat (JIT) mengkompilasi sendiri. Dalam kasus Maxine, JIT akan mengkompilasi fase boot-nya sendiri, kemudian membuat serial yang mengkompilasi kode asli ke image VM bootstrap dan menempelkan bootloader yang sangat sederhana di depan (satu-satunya komponen VM yang ditulis dalam C, meskipun itu hanya untuk kenyamanan , bisa juga di Jawa). Sekarang Anda dapat menggunakan Maxine untuk mengeksekusi sendiri.

Jörg W Mittag
sumber
Ya . Saya tidak pernah tahu dunia penerjemah self-hosting begitu lengket! Terima kasih telah memberikan ikhtisar yang bagus.
Christian Dean
1
Haha, yah, mengapa dunia tidak begitu membekas dari konsep? ;-)
Jörg W Mittag
3
Saya kira salah satu masalah adalah orang sering bermain cepat dan longgar dengan bahasa yang terlibat. Sebagai contoh, Rubinius biasanya disebut "Ruby di Ruby", tetapi itu hanya setengah dari cerita. Ya, tegasnya , compiler Ruby di Rubinius ditulis dalam Ruby, namun VM yang mengeksekusi bytecode tidak. Dan lebih buruk lagi: PyPy sering disebut "Python in Python", kecuali sebenarnya tidak ada satu baris Python di sana. Semuanya ditulis dalam RPython, yang dirancang agar tidak asing bagi programmer Python, tetapi bukan Python . Demikian juga SqueakVM: tidak ditulis dalam Smalltalk, itu ...
Jörg W Mittag
... ditulis dalam bahasa gaul, yang menurut orang-orang yang benar-benar diberi kode di dalamnya, bahkan lebih buruk daripada C dalam kemampuan abstraksinya. Satu-satunya kelebihan yang dimiliki Slang, adalah bahwa itu adalah bagian yang tepat dari Smalltalk yang berarti bahwa Anda dapat mengembangkannya (dan menjalankan dan yang paling penting men-debug VM pada) IDE Smalltalk yang kuat.
Jörg W Mittag
2
Hanya untuk menambah kerumitan lain: Beberapa bahasa yang ditafsirkan (misalnya FORTH, dan mungkin TeX) mampu menulis gambar memori yang dapat dimuat dari sistem yang sedang berjalan, sebagai file yang dapat dieksekusi. Dalam pengertian itu, sistem seperti itu kemudian dapat berjalan tanpa penerjemah asli. Sebagai contoh, saya pernah menulis penerjemah FORTH dengan menggunakan versi 16-bit FORTH untuk "menafsirkan silang" versi 32-bit untuk CPU yang berbeda, dan untuk berjalan pada OS yang berbeda. (Catatan, bahasa FORTH mencakup assemblernya sendiri, sehingga "FORTH VM" (yang biasanya hanya 10 atau 20 instruksi kode mesin) dapat ditulis dalam FORTH itu sendiri.)
alephzero
7

Anda benar dalam mencatat bahwa juru bahasa hosting sendiri masih membutuhkan juru bahasa untuk menjalankannya sendiri, dan tidak dapat di-bootstrap dalam arti yang sama dengan kompiler.

Namun, diri-host bahasa bukanlah hal yang sama sebagai juru diri-host. Biasanya lebih mudah membangun interpreter daripada membangun kompiler. Oleh karena itu, untuk mengimplementasikan bahasa baru, kami mungkin pertama-tama mengimplementasikan seorang juru bahasa dalam bahasa yang tidak terkait. Kemudian kita bisa menggunakan juru bahasa itu untuk mengembangkan kompiler untuk bahasa kita. Bahasa tersebut kemudian di-host-sendiri, karena kompiler ditafsirkan. Compiler kemudian dapat mengkompilasi sendiri, dan kemudian dapat dianggap sepenuhnya bootstrap.

Kasus khusus dari ini adalah runtime kompilasi JIT self-hosting. Itu bisa dimulai dengan juru bahasa dalam bahasa host, yang kemudian menggunakan bahasa baru untuk mengimplementasikan kompilasi JIT, setelah itu kompiler JIT dapat mengkompilasi dirinya sendiri. Ini terasa seperti interpreter hosting sendiri, tetapi menghindari masalah penerjemah tak terbatas. Pendekatan ini digunakan, tetapi belum terlalu umum.

Teknik terkait lainnya adalah interpreter yang dapat diperluas, di mana kita dapat membuat ekstensi dalam bahasa yang ditafsirkan. Misalnya, kami mungkin menerapkan opcode baru dalam bahasa. Ini dapat mengubah juru bahasa telanjang menjadi juru bahasa kaya fitur, selama kita menghindari ketergantungan sirkular.

Sebuah kasus yang benar-benar terjadi cukup umum adalah kemampuan bahasa untuk mempengaruhi penguraiannya sendiri, misalnya sebagai makro yang dievaluasi waktu-parsing. Karena bahasa makro sama dengan bahasa yang sedang diproses, bahasa ini cenderung jauh lebih kaya fitur daripada bahasa makro khusus atau terbatas. Namun, benar untuk dicatat bahwa bahasa yang melakukan ekstensi adalah bahasa yang sedikit berbeda dari bahasa setelah ekstensi.

Ketika interpreter yang di-host secara nyata digunakan, ini biasanya dilakukan untuk alasan pendidikan atau penelitian. Misalnya, menerapkan juru bahasa untuk Skema di dalam Skema adalah cara yang keren untuk mengajarkan bahasa pemrograman (lihat SICP).

amon
sumber