Apakah seorang juru bahasa menghasilkan kode mesin?

42

Saya mempelajari topik kompiler dan juru bahasa secara intensif. Saya ingin memeriksa apakah pemahaman dasar saya benar, jadi mari kita asumsikan yang berikut:

Saya memiliki bahasa yang disebut "Foobish" dan kata kuncinya adalah

<OUTPUT> 'TEXT', <Number_of_Repeats>;

Jadi jika saya ingin mencetak ke konsol 10 kali, saya akan menulis

OUTPUT 'Hello World', 10;

Hello World.foobish-file.

Sekarang saya menulis penerjemah dalam bahasa pilihan saya - C # dalam hal ini:

using System;

namespace FoobishInterpreter
{
    internal class Program
    {
        private static void Main(string[] args)
        {
            analyseAndTokenize(Hello World.foobish-file)//Pseudocode
            int repeats = Token[1];
            string outputString = Token[0];
            for (var i = 0; i < repeats; i++)
            {
                Console.WriteLine(outputString);
            }
        }
    }
}

Pada tingkat penerjemah yang sangat mudah, penerjemah akan menganalisis file skrip, dll. Dan mengeksekusi foobish-bahasa dengan cara implementasi penerjemah.

Akankah kompiler membuat bahasa mesin yang berjalan pada perangkat keras fisik secara langsung?

Jadi seorang penerjemah tidak menghasilkan bahasa mesin, tetapi apakah kompiler melakukannya untuk inputnya?

Apakah saya memiliki kesalahpahaman dalam cara dasar bagaimana kompiler dan penerjemah bekerja?

GrayFox
sumber
21
Menurut Anda apa yang dilakukan oleh C # "compiler"? Sebagai petunjuk, itu tidak menghasilkan kode mesin.
Philip Kendall
3
Kompiler Java menghasilkan kode untuk JVM. Jadi mesin target kompiler dapat menjadi mesin virtual yang tidak dieksekusi langsung oleh perangkat keras. Perbedaan utama antara interpreter dan kompiler adalah bahwa kompiler pertama-tama memeriksa dan menerjemahkan seluruh kode sumber ke dalam bahasa mesin target. Kode yang dikompilasi ini kemudian dieksekusi oleh mesin yang dimaksudkan untuknya. Di sisi lain, seorang juru bahasa akan menerjemahkan dan mengeksekusi potongan-potongan program Anda dengan cepat.
Giorgio
@Iorgio: Maksud Anda, seperti JIT?
Robert Harvey
2
@ RobertTarvey: Maksud saya Java Compiler (javac): sejauh yang saya tahu itu menghasilkan bytecode untuk JVM. Dan, sekali lagi AFAIK, JIT nanti (saat runtime) mengkompilasi beberapa bytecode yang digunakan sangat sering ke dalam bahasa mesin asli.
Giorgio
4
kompiler berarti menerjemahkan. Itu dapat memancarkan semua jenis bahasa: c, assembly, javascript, kode mesin.
Esben Skov Pedersen

Jawaban:

77

Istilah "interpreter" dan "compiler" jauh lebih kabur daripada sebelumnya. Bertahun-tahun yang lalu, lebih umum bagi kompiler untuk menghasilkan kode mesin untuk dieksekusi nanti, sementara penerjemah kurang lebih "mengeksekusi" kode sumber secara langsung. Jadi kedua istilah itu dipahami dengan baik saat itu.

Tetapi hari ini ada banyak variasi dalam penggunaan "kompiler" dan "penerjemah." Sebagai contoh, VB6 "mengkompilasi" ke kode byte (bentuk Bahasa Antara ), yang kemudian "ditafsirkan" oleh VB Runtime. Proses serupa terjadi di C #, yang menghasilkan CIL yang kemudian dieksekusi oleh Just-In-Time Compiler (JIT) yang, di masa lalu, akan dianggap sebagai interpreter. Anda dapat "membekukan-kering" output JIT ke dalam biner aktual yang dapat dieksekusi dengan menggunakan NGen.exe , produk yang akan menjadi hasil kompiler di masa lalu.

Jadi jawaban untuk pertanyaan Anda tidak semudah dulu.

Bacaan Lanjutan
Compiler vs Interpreter di Wikipedia

Robert Harvey
sumber
6
@ Giorgio: Kebanyakan penafsir saat ini tidak benar-benar mengeksekusi kode sumber, melainkan output dari AST atau yang serupa. Kompiler memiliki proses yang serupa. Perbedaannya hampir tidak jelas seperti yang Anda pikirkan.
Robert Harvey
5
"Anda dapat" membekukan-kering "output dari JIT ke dalam biner aktual yang dapat dieksekusi dengan menggunakan NGen.exe, produk yang akan menjadi hasil kompiler di masa lalu.": Tapi masih hari ini hasilnya kompiler (yaitu, kompiler just-in-time). Tidak masalah ketika kompiler dijalankan, tetapi apa yang dilakukannya. Compiler mengambil sebagai input representasi dari sepotong kode dan menampilkan representasi baru. Seorang juru bahasa akan menampilkan hasil dari mengeksekusi potongan kode itu. Ini adalah dua proses yang berbeda, tidak peduli bagaimana Anda mencampurnya dan ketika Anda menjalankan apa.
Giorgio
4
"Kompiler" hanyalah istilah yang mereka pilih untuk dilampirkan ke GCC. Mereka memilih untuk tidak menyebut NGen sebagai kompiler, meskipun ia menghasilkan kode mesin, lebih memilih untuk melampirkan istilah itu ke langkah sebelumnya, yang bisa dibilang bisa disebut interpreter, meskipun itu menghasilkan kode mesin (beberapa penerjemah juga melakukannya). Maksud saya adalah bahwa saat ini tidak ada prinsip yang mengikat yang dapat Anda panggil untuk secara definitif menyebut sesuatu sebagai kompiler atau penerjemah, selain "itulah yang selalu mereka sebut sebagai".
Robert Harvey
4
Seperti pemahaman saya yang sangat terbatas, hari ini x86 CPU setengah jalan untuk menjadi mesin JIT berbasis perangkat keras, dengan perakitan yang memiliki hubungan yang terus memudar dengan apa yang sebenarnya dieksekusi.
Leushenko
4
@ RobertTarvey sementara saya setuju bahwa tidak ada garis pemisah yang jelas antara teknik yang digunakan dalam interpreter dan kompiler, ada pembagian fungsi yang cukup jelas: jika hasil dari mengeksekusi alat yang diberikan dengan kode program sebagai input adalah pelaksanaan itu program, alat ini adalah seorang juru bahasa. Jika hasilnya adalah output dari terjemahan program ke dalam bentuk yang kurang abstrak, itu adalah kompiler. Jika hasilnya terjemahan ke bentuk yang lebih abstrak, itu jika dekompiler. Namun, kasus-kasus di mana lebih dari satu dari hasil ini bersifat ambigu.
Jules
34

Ringkasan yang saya berikan di bawah ini didasarkan pada "Compiler, Prinsip, Teknik, & Peralatan", Aho, Lam, Sethi, Ullman, (Pearson International Edition, 2007), halaman 1, 2, dengan tambahan beberapa ide saya sendiri.

Dua mekanisme dasar untuk memproses suatu program adalah kompilasi dan interpretasi .

Kompilasi sebagai input program sumber dalam bahasa yang diberikan dan output program target dalam bahasa target.

source program --> | compiler | --> target program

Jika bahasa target adalah kode mesin, itu dapat dieksekusi langsung pada beberapa prosesor:

input --> | target program | --> output

Kompilasi melibatkan pemindaian dan menerjemahkan seluruh program input (atau modul) dan tidak melibatkan pelaksanaannya.

Interpretasi mengambil sebagai input program sumber dan inputnya, dan menghasilkan output program sumber

source program, input --> | interpreter | --> output

Interpretasi biasanya melibatkan pemrosesan (analisis dan eksekusi) program satu per satu pernyataan.

Dalam praktiknya, banyak pengolah bahasa menggunakan campuran dari dua pendekatan. Misalnya, program Java pertama-tama diterjemahkan (dikompilasi) menjadi program perantara (kode byte):

source program --> | translator | --> intermediate program

output dari langkah ini kemudian dieksekusi (ditafsirkan) oleh mesin virtual:

intermediate program + input --> | virtual machine | --> output

Untuk memperumit hal-hal lebih jauh, JVM dapat melakukan kompilasi just-in-time pada saat runtime untuk mengkonversi kode byte ke format lain, yang kemudian dieksekusi.

Juga, bahkan ketika Anda mengkompilasi ke bahasa mesin, ada penerjemah yang menjalankan file biner Anda yang diimplementasikan oleh prosesor yang mendasarinya. Oleh karena itu, bahkan dalam kasus ini Anda menggunakan hibrida kompilasi + interpretasi.

Jadi, sistem nyata menggunakan campuran keduanya sehingga sulit untuk mengatakan apakah prosesor bahasa yang diberikan adalah kompiler atau juru bahasa, karena mungkin akan menggunakan kedua mekanisme pada berbagai tahap pemrosesan. Dalam hal ini mungkin akan lebih tepat untuk menggunakan istilah lain yang lebih netral.

Namun demikian, kompilasi dan interpretasi adalah dua jenis proses yang berbeda, seperti yang dijelaskan dalam diagram di atas,

Untuk menjawab pertanyaan awal.

Kompiler akan membuat bahasa mesin yang berjalan pada perangkat keras fisik secara langsung?

Belum tentu, kompiler menerjemahkan program yang ditulis untuk mesin M1 ke program yang setara yang ditulis untuk mesin M2. Mesin target dapat diimplementasikan dalam perangkat keras atau menjadi mesin virtual. Secara konseptual tidak ada perbedaan. Poin penting adalah bahwa kompiler melihat sepotong kode dan menerjemahkannya ke bahasa lain tanpa menjalankannya.

Jadi seorang penerjemah tidak menghasilkan bahasa mesin tetapi seorang kompiler melakukannya untuk inputnya?

Jika dengan memproduksi Anda merujuk ke output, maka kompiler menghasilkan program target yang mungkin dalam bahasa mesin, seorang penerjemah tidak.

Giorgio
sumber
7
Dengan kata lain: penerjemah mengambil program P dan menghasilkan output O, kompiler mengambil P dan menghasilkan program P ′ yang menghasilkan O; interpreter sering menyertakan komponen yang merupakan kompiler (misalnya, ke bytecode, representasi perantara, atau instruksi mesin JIT) dan juga kompiler dapat menyertakan juru bahasa (misalnya, untuk mengevaluasi perhitungan waktu kompilasi).
Jon Purdy
"kompiler dapat menyertakan juru bahasa (mis., untuk mengevaluasi perhitungan waktu kompilasi)": Poin bagus. Saya kira template Lisp macro dan C ++ mungkin sudah diproses sebelumnya dengan cara ini.
Giorgio
Lebih sederhana lagi, preprocessor C mengkompilasi kode sumber C dengan arahan CPP ke dalam plain C, dan menyertakan juru bahasa untuk ekspresi boolean seperti defined A && !defined B.
Jon Purdy
@ JonPurdy Saya akan setuju dengan itu, tapi saya juga akan menambahkan kelas, "penerjemah tradisional", yang tidak menggunakan representasi perantara di luar mungkin versi tokenized dari sumber. Contohnya adalah kerang, banyak BASIC, Lisp klasik, Tcl sebelum 8.0, dan bc.
hobbs
1
@naxa - lihat jawaban Lawrence dan komentar Paul Draper tentang jenis kompiler. Assembler adalah jenis kompiler khusus di mana (1) bahasa output dimaksudkan untuk eksekusi langsung oleh mesin atau mesin virtual dan (2) ada korespondensi satu-ke-satu yang sangat sederhana antara pernyataan input dan instruksi output.
Jules
22

Kompiler akan membuat bahasa mesin

No Sebuah compiler hanyalah sebuah program yang mengambil sebagai masukan program yang ditulis dalam bahasa A dan menghasilkan sebagai output program semantik setara dalam bahasa B . Bahasa B bisa apa saja, tidak harus bahasa mesin.

Kompiler dapat mengkompilasi dari bahasa tingkat tinggi ke bahasa tingkat tinggi lainnya (misalnya GWT, yang mengkompilasi Java ke ECMAScript), dari bahasa tingkat tinggi ke bahasa tingkat rendah (misalnya Gambit, yang menyusun Skema ke C), dari bahasa tingkat tinggi ke kode mesin (misalnya GCJ, yang mengkompilasi Jawa ke kode asli), dari bahasa tingkat rendah ke bahasa tingkat tinggi (misalnya Clue, yang mengkompilasi C ke Java, Lua, Perl, ECMAScript dan Umum Lisp), dari bahasa tingkat rendah ke bahasa tingkat rendah lainnya (misalnya Android SDK, yang mengkompilasi kode byte JVML ke kode byte Dalvik), dari bahasa tingkat rendah ke kode mesin (mis. Kompiler C1X yang merupakan bagian dari HotSpot, yang mengkompilasi bytecode JVML ke kode mesin), kode mesin ke bahasa tingkat tinggi (apa pun yang disebut "dekompiler", juga Emscripten, yang mengkompilasi kode mesin LLVM ke ECMAScript),kode mesin ke bahasa tingkat rendah (misalnya kompiler JIT dalam JPC, yang mengkompilasi kode asli x86 ke bytecode JVML) dan kode asli ke kode asli (mis. kompiler JIT di PearPC, yang mengkompilasi kode asli PowerPC ke kode asli x86).

Perhatikan juga bahwa "kode mesin" adalah istilah yang benar-benar kabur karena beberapa alasan. Misalnya, ada CPU yang secara asli mengeksekusi kode byte JVM, dan ada penerjemah perangkat lunak untuk kode mesin x86. Jadi, apa yang membuat satu "kode mesin asli" tetapi tidak yang lain? Juga, setiap bahasa adalah kode untuk mesin abstrak untuk bahasa itu.

Ada banyak nama khusus untuk kompiler yang menjalankan fungsi khusus. Terlepas dari kenyataan bahwa ini adalah nama khusus, semua ini masih kompiler, hanya jenis kompiler khusus:

  • jika bahasa A dianggap pada tingkat abstraksi yang kira-kira sama dengan bahasa B , kompiler dapat disebut transpiler (misalnya Ruby-to-ECMAScript-transpiler atau ECMAScript2015-to-ECMAScript5-transpiler)
  • jika bahasa A dianggap berada pada tingkat abstraksi yang lebih rendah daripada bahasa B , kompiler dapat disebut dekompiler (mis. dekompiler kode-x-mesin-ke-C-mesin)
  • jika bahasa A == bahasa B , kompiler dapat disebut pengoptimal , obfuscator , atau minifier (tergantung pada fungsi tertentu dari kompiler)

yang berjalan pada perangkat keras fisik secara langsung?

Belum tentu. Itu bisa dijalankan dalam juru bahasa atau dalam VM. Itu bisa dikompilasi lebih lanjut ke bahasa yang berbeda.

Jadi seorang penerjemah tidak menghasilkan bahasa mesin tetapi seorang kompiler melakukannya untuk inputnya?

Seorang juru bahasa tidak menghasilkan apa-apa. Itu hanya menjalankan program.

Kompiler menghasilkan sesuatu, tetapi tidak harus berupa bahasa mesin, bisa berupa bahasa apa pun. Bahkan bisa bahasa yang sama dengan bahasa input! Sebagai contoh, Supercompiler, LLC memiliki kompiler yang mengambil Java sebagai inputnya dan menghasilkan Java yang dioptimalkan sebagai outputnya. Ada banyak kompiler ECMAScript yang mengambil ECMAScript sebagai input dan menghasilkan ECMAScript yang dioptimalkan, diperkecil, dan dikaburkan sebagai outputnya.


Anda mungkin juga tertarik dengan:

Jörg W Mittag
sumber
16

Saya pikir Anda harus membuang gagasan "kompiler versus juru bahasa" sepenuhnya, karena ini merupakan dikotomi yang salah.

  • Sebuah compiler adalah transformator : Ini mengubah program komputer yang ditulis dalam bahasa sumber dan output yang setara dalam bahasa target . Biasanya, bahasa sumber adalah level yang lebih tinggi daripada bahasa target - dan jika sebaliknya, kita sering menyebut transformator semacam itu sebagai dekompiler .
  • Seorang juru bahasa adalah mesin eksekusi . Menjalankan program komputer yang ditulis dalam satu bahasa, sesuai dengan spesifikasi bahasa itu. Kami kebanyakan menggunakan istilah untuk perangkat lunak (tetapi dengan cara tertentu, CPU klasik dapat dilihat sebagai "juru bahasa" berbasis perangkat keras untuk kode mesinnya).

Kata kolektif untuk membuat bahasa pemrograman abstrak bermanfaat di dunia nyata adalah implementasi .

Di masa lalu, implementasi bahasa pemrograman sering terdiri dari hanya sebuah kompiler (dan CPU yang dihasilkannya kode) atau hanya seorang juru bahasa - sehingga mungkin tampak seperti dua jenis alat yang saling eksklusif. Hari ini, Anda dapat dengan jelas melihat bahwa ini bukan masalahnya (dan itu tidak pernah dimulai dengan). Mengambil implementasi bahasa pemrograman yang canggih, dan berusaha untuk mendorong nama "kompiler" atau "juru bahasa" untuk itu, sering kali akan membawa Anda ke hasil yang tidak meyakinkan atau tidak konsisten.

Implementasi bahasa pemrograman tunggal dapat melibatkan sejumlah kompiler dan juru bahasa , seringkali dalam berbagai bentuk (standalone, on-the-fly), sejumlah alat lainnya, seperti analisa statis dan pengoptimal , dan sejumlah langkah. Bahkan dapat mencakup seluruh implementasi dari sejumlah bahasa perantara (yang mungkin tidak terkait dengan yang sedang diterapkan).

Contoh skema implementasi meliputi:

  • Kompiler AC yang mengubah kode mesin C ke x86, dan CPU x86 yang mengeksekusi kode itu.
  • Kompiler AC yang mengubah C menjadi LLVM IR, kompiler backend LLVM yang mengubah LLVM IR menjadi kode mesin x86, dan CPU x86 yang mengeksekusi kode itu.
  • Kompiler AC yang mengubah C menjadi LLVM IR, dan penerjemah LLVM yang mengeksekusi LLVM IR.
  • Kompiler Java yang mentransformasikan Java menjadi JVM bytecode, dan JRE dengan interpreter yang mengeksekusi kode itu.
  • Kompiler Java yang mentransformasikan Java menjadi JVM bytecode, dan JRE dengan interpreter yang mengeksekusi beberapa bagian dari kode itu dan kompiler yang mengubah bagian lain dari kode itu menjadi kode mesin x86, dan CPU x86 yang mengeksekusi kode itu.
  • Kompiler Java yang mengubah Java menjadi JVM bytecode, dan CPU ARM yang mengeksekusi kode itu.
  • Kompiler AC # yang mengubah C # menjadi CIL, CLR dengan kompiler yang mengubah CIL menjadi kode mesin x86, dan CPU x86 yang mengeksekusi kode itu.
  • Juru bahasa Ruby yang mengeksekusi Ruby.
  • Lingkungan Ruby dengan interpreter yang mengeksekusi Ruby dan kompiler yang mengubah Ruby menjadi kode mesin x86, dan CPU x86 yang mengeksekusi kode itu.

...dan seterusnya.

Theodoros Chatzigiannakis
sumber
+1 untuk menunjukkan bahwa bahkan penyandian yang dirancang untuk representasi menengah (misalnya bytecode java) dapat memiliki implementasi perangkat keras.
Jules
7

Sementara garis antara kompiler dan interpreter menjadi kabur dari waktu ke waktu, orang masih dapat menarik garis di antara mereka dengan melihat semantik dari apa yang harus dilakukan oleh program dan apa yang dilakukan oleh kompiler / juru bahasa.

Kompiler akan menghasilkan program lain (biasanya dalam bahasa tingkat rendah seperti kode mesin) yang, jika program itu dijalankan, akan melakukan apa yang seharusnya dilakukan oleh program Anda.

Seorang juru bahasa akan melakukan apa yang seharusnya dilakukan oleh program Anda.

Dengan definisi ini, tempat-tempat di mana fuzzy menjadi kasus di mana kompiler / juru bahasa Anda dapat dianggap melakukan hal-hal yang berbeda tergantung pada bagaimana Anda melihatnya. Misalnya, Python mengambil kode Python Anda dan mengkompilasinya menjadi bytecode Python yang dikompilasi. Jika bytecode Python ini dijalankan melalui penerjemah bytecode Python , ia melakukan apa yang seharusnya dilakukan oleh program Anda. Namun, dalam sebagian besar situasi, pengembang Python menganggap kedua langkah tersebut dilakukan dalam satu langkah besar, sehingga mereka memilih untuk menganggap juru bahasa CPython sebagai mengartikan kode sumber mereka, dan fakta bahwa itu dikompilasi sepanjang jalan dianggap sebagai detail implementasi . Dengan cara ini, itu semua masalah perspektif.

Cort Ammon
sumber
5

Berikut adalah disambiguasi konseptual sederhana antara kompiler dan penerjemah.

Pertimbangkan 3 bahasa: bahasa pemrograman , P (ditulis dalam program apa); bahasa domain , D (untuk apa yang terjadi dengan program yang sedang berjalan); dan bahasa target , T (beberapa bahasa ketiga).

Secara konseptual,

  • sebuah compiler menerjemahkan P ke T sehingga Anda dapat mengevaluasi T (D); sedangkan

  • seorang juru bahasa mengevaluasi P (D) secara langsung.

Lawrence
sumber
1
Kebanyakan penerjemah modern sebenarnya tidak mengevaluasi bahasa sumber secara langsung, tetapi lebih merupakan representasi perantara dari bahasa sumber.
Robert Harvey
4
@RobertHarvey Itu tidak mengubah perbedaan konseptual antara istilah.
Lawrence
1
Jadi yang Anda maksud sebagai penerjemah adalah bagian yang mengevaluasi representasi perantara. Bagian yang membuat representasi perantara adalah kompiler , menurut definisi Anda.
Robert Harvey
6
@RobertHarvey Tidak juga. Persyaratan tergantung pada tingkat abstraksi tempat Anda bekerja. Jika Anda melihat di bawahnya, alat itu bisa melakukan apa saja. Dengan analogi, katakan Anda pergi ke negara asing dan membawa Bob, teman bilingual. Jika Anda berkomunikasi dengan penduduk setempat dengan berbicara dengan Bob yang pada gilirannya berbicara dengan penduduk setempat, Bob bertindak sebagai juru bahasa bagi Anda (bahkan jika ia menulis dengan bahasa mereka sebelum berbicara). Jika Anda bertanya pada Bob untuk frasa dan Bob menulisnya dalam bahasa asing, dan Anda berkomunikasi dengan penduduk setempat dengan merujuk pada tulisan-tulisan itu (bukan Bob), Bob bertindak sebagai penyusun untuk Anda.
Lawrence
1
Jawaban yang sangat bagus. Perlu dicatat: Saat ini Anda mungkin mendengar "transpiler". Itu adalah kompiler di mana P dan T adalah tingkat abstraksi yang serupa, untuk beberapa definisi yang serupa. (Misalnya, ES5 ke ES6 transpiler.)
Paul Draper