Meneruskan kode C adalah kebiasaan yang sudah sangat mapan. C asli dengan kelas (dan implementasi C ++ awal, kemudian disebut Cfront ) berhasil melakukannya. Beberapa implementasi Lisp atau Skema melakukan hal itu, misalnya Skema Ayam , Skema48 , Bigloo . Beberapa orang diterjemahkan Prolog ke C . Dan begitu pula beberapa versi Mozart (dan ada upaya untuk mengkompilasi bytecode Ocaml ke C ). Sistem kecerdasan buatan J.Pitrat, CAIA, juga bootstrap dan menghasilkan semua kode C-nya. Vala juga diterjemahkan ke C, untuk kode terkait GTK. Buku Queinnec, Lisp In Small Pieces memiliki beberapa bab tentang terjemahan ke C.
Salah satu masalah saat menerjemahkan ke C adalah panggilan berulang-ulang . Standar C tidak menjamin bahwa kompiler C menerjemahkannya dengan benar (menjadi "lompatan dengan argumen", yaitu tanpa memakan tumpukan panggilan), bahkan jika dalam beberapa kasus, versi terbaru GCC (atau Dentang / LLVM) melakukan optimasi itu .
Masalah lainnya adalah pengumpulan sampah . Beberapa implementasi hanya menggunakan pengumpul sampah konservatif Boehm (yang ramah-C ...). Jika Anda ingin mengumpulkan kode sampah (seperti beberapa implementasi Lisp lakukan, misalnya SBCL) yang mungkin menjadi mimpi buruk (Anda ingin dlclose
di Posix).
Namun masalah lain adalah berurusan dengan kelanjutan kelas satu dan panggilan / cc . Tapi trik pintar mungkin dilakukan (lihat di dalam Skema Ayam). Mengakses tumpukan panggilan bisa memerlukan banyak trik (tapi lihat GNU backtrace , dll ....). Kegigihan ortogonal dari kelanjutan (yaitu tumpukan atau benang) akan sulit di C.
Penanganan pengecualian sering merupakan masalah untuk memancarkan panggilan pintar ke longjmp dll ...
Anda mungkin ingin menghasilkan (dalam kode C Anda yang dipancarkan) #line
arahan yang sesuai . Ini membosankan dan membutuhkan banyak pekerjaan (Anda akan ingin itu misalnya menghasilkan gdb
kode yang lebih mudah -debuggable).
Saya MELT lispy bahasa domain yang spesifik (untuk menyesuaikan atau memperpanjang GCC ) diterjemahkan ke C (sebenarnya untuk miskin C ++ sekarang). Ia memiliki pengumpul sampah penyalinan generasinya sendiri. (Anda mungkin tertarik oleh Qish atau Ravenbrook MPS ). Sebenarnya, GC generasi lebih mudah dalam kode C yang dihasilkan mesin daripada kode C yang ditulis tangan (karena Anda akan menyesuaikan generator kode C Anda untuk penghalang tulis dan mesin GC Anda).
Saya tidak tahu implementasi bahasa apa pun yang diterjemahkan ke kode C ++ asli , yaitu menggunakan beberapa teknik "pengumpulan sampah waktu" untuk memancarkan kode C ++ menggunakan banyak template STL dan menghormati idiom RAII . (tolong beri tahu jika Anda tahu satu).
Apa yang lucu hari ini adalah bahwa (pada desktop Linux saat ini) kompiler C mungkin cukup cepat untuk mengimplementasikan loop read-eval-print- level interaktif yang diterjemahkan ke C: Anda akan memancarkan kode C (beberapa ratus baris) pada setiap pengguna interaksi, Anda akan fork
kompilasi menjadi objek bersama, yang kemudian Anda lakukan dlopen
. (MELT melakukan semuanya sudah siap, dan biasanya cukup cepat). Semua ini mungkin memerlukan beberapa persepuluh detik dan dapat diterima oleh pengguna akhir.
Jika memungkinkan, saya akan merekomendasikan untuk menerjemahkan ke C, bukan ke C ++, khususnya karena kompilasi C ++ lambat.
Jika Anda menerapkan bahasa Anda, Anda mungkin juga mempertimbangkan (alih-alih memancarkan kode C) beberapa pustaka JIT seperti libjit , GNU lightning , asmjit , atau bahkan LLVM atau GCCJIT . Jika Anda ingin menerjemahkan ke C, Anda terkadang menggunakan tinycc : ia mengkompilasi dengan sangat cepat kode C yang dihasilkan (bahkan dalam memori) untuk memperlambat kode mesin. Tetapi secara umum Anda ingin mengambil keuntungan dari optimasi yang dilakukan oleh kompiler C nyata seperti GCC
Jika Anda menerjemahkan ke bahasa C Anda, pastikan untuk membangun AST seluruh kode C yang dihasilkan dalam memori terlebih dahulu (ini juga membuat lebih mudah untuk menghasilkan semua deklarasi terlebih dahulu, lalu semua definisi dan kode fungsi). Anda dapat melakukan beberapa optimasi / normalisasi dengan cara ini. Anda juga dapat tertarik pada beberapa ekstensi GCC (mis. Goto yang dihitung). Anda mungkin ingin menghindari menghasilkan fungsi C yang sangat besar - misalnya dari seratus ribu garis C yang dihasilkan - (Anda sebaiknya membaginya menjadi bagian-bagian yang lebih kecil) karena mengoptimalkan kompiler C sangat tidak senang dengan fungsi C yang sangat besar (dalam praktiknya, dan secara eksperimental,gcc -O
waktu kompilasi fungsi besar sebanding dengan kuadrat ukuran kode fungsi). Jadi batasi ukuran fungsi C yang Anda buat masing-masing beberapa ribu baris.
Perhatikan bahwa kedua dentang (melalui LLVM ) dan GCC (melalui libgccjit ) C & C ++ compiler menawarkan beberapa cara untuk memancarkan beberapa representasi internal yang cocok untuk compiler ini, namun hal ini kekuatan (atau tidak) lebih sulit daripada memancarkan C (atau C ++) kode, dan khusus untuk setiap kompiler.
Jika mendesain bahasa yang akan diterjemahkan ke C, Anda mungkin ingin memiliki beberapa trik (atau konstruksi) untuk menghasilkan campuran C dengan bahasa Anda. Kertas DSL2011 saya MELT : Bahasa Tertentu Domain Diterjemahkan yang tertanam dalam GCC Compiler akan memberi Anda petunjuk yang bermanfaat.