Meninjau Kembali Let’s Build a Compiler
(eli.thegreenplace.net)- Proyek yang mengimplementasikan ulang tutorial ‘Let’s Build a Compiler’ karya Jack Crenshaw yang diterbitkan pada 1988~1995 dengan Python dan WebAssembly
- Versi aslinya menggunakan Pascal dan assembly Motorola 68000, tetapi dalam pengerjaan kali ini diubah menjadi kode yang dapat dijalankan di lingkungan modern
- Inti tutorial ini adalah mengimplementasikan sendiri recursive-descent parser dan menghasilkan kode assembly nyata sejak tahap awal
- Pendekatan Crenshaw menggunakan syntax-directed translation, tetapi menunjukkan keterbatasan pada tahap penanganan tipe
- Implementasi ulang kali ini bermakna karena memulihkan tutorial klasik tersebut ke bentuk yang bisa dijalankan dan dieksperimenkan langsung oleh pengembang modern
Latar belakang tutorial klasik
- ‘Let’s Build a Compiler’ karya Jack Crenshaw adalah panduan pengantar pembuatan compiler yang diterbitkan antara 1988~1995 dan masih sering dirujuk hingga sekarang
- Teks aslinya ditulis dalam Pascal dan menghasilkan kode assembly Motorola 68000
- Bahkan setelah 35 tahun, pada 2025 pun masih terus dibicarakan di Hacker News dan tempat lain
- Penulis artikel pertama kali menemukan tutorial ini pada 2003 dan sangat terkesan, lalu pada 2025 memindahkannya lagi ke Python dan WebAssembly
Implementasi ulang ke Python dan WebAssembly
- Tidak berhenti pada sekadar membaca, penulis menerjemahkan compiler dalam tutorial ke Python dan mengimplementasikannya agar menghasilkan WebAssembly (WASM)
- Hasilnya dipublikasikan di repositori GitHub(eliben/letsbuildacompiler)
- Berkas
TUTORIAL.mdmenjelaskan bagaimana setiap bagian dari versi asli dipetakan ke kode Python - Pengguna dapat membaca tutorial aslinya sambil bereksperimen dengan kode yang benar-benar bisa dijalankan
- Berkas
Contoh bahasa KISS dan keluaran WASM
- Ditampilkan hasil kompilasi program contoh dalam bahasa KISS yang dirancang Crenshaw menggunakan compiler versi Python
- Contohnya mencakup
procedure, loopwhile, serta pass-by-value dan pass-by-reference
- Contohnya mencakup
- Teks WASM yang dihasilkan mencakup logika penanganan parameter referensi (by-reference parameter), dengan hampir tidak ada optimisasi yang diterapkan
- Bagian di mana variabel global
Xdikembalikan secara implisit dari fungsimainadalah mekanisme untuk memudahkan pengujian - Kode WASM yang dihasilkan digunakan dalam pengujian untuk memverifikasi hasil yang diharapkan melalui eksekusi nyata
Kekuatan tutorial
- Tutorial Crenshaw membangun recursive-descent parser secara bertahap dan berfokus pada pembuatan kode yang benar-benar berjalan alih-alih teori yang rumit
- Pada masa itu
lexdanyaccdianggap standar, tetapi kesederhanaan dan kejelasan parser buatan tangan memberi pengaruh besar - Selama 20 tahun berikutnya, penulis pun menjadi lebih menyukai recursive-descent parser manual
- Pada masa itu
- Selain itu, ketika kebanyakan materi kuliah saat itu berfokus pada parsing, Crenshaw sudah membahas generasi kode assembly sejak tahap awal
Keterbatasan dan kemungkinan perbaikan
- Tutorial ini menggunakan pendekatan syntax-directed translation, yaitu menghasilkan kode langsung saat proses parsing
- Pendekatan ini berguna untuk belajar, tetapi memiliki keterbatasan karena informasi tipe tidak diketahui sebelumnya sehingga optimisasi menjadi sulit
- Khususnya setelah tipe diperkenalkan pada bagian 14 dan seterusnya, terlihat bahwa pendekatan yang lebih terstruktur dengan representasi menengah (IR) atau AST diperlukan
- Tidak dijelaskan secara eksplisit mengapa Crenshaw menghentikan tutorial setelah bagian 14, tetapi disebutkan bahwa kemungkinan ada kaitannya dengan keterbatasan ini
- Penulis menilai bahwa bagian 14 adalah titik balik untuk beralih ke pendekatan yang membuat AST lebih dulu, lalu melakukan pemeriksaan tipe dan generasi kode
Kesimpulan
- Tutorial aslinya tetap mempertahankan keterbacaan dan kepraktisan yang sangat baik sebagai pengantar pembuatan compiler
- Versi Python·WASM kali ini adalah implementasi pelengkap agar pengembang modern bisa belajar tanpa teknologi lama
- Siapa pun dapat bereksperimen lewat repositori GitHub, dan ini dinilai sebagai reinterpretasi modern yang memungkinkan orang merasakan langsung struktur compiler
1 komentar
Komentar Hacker News
Saya suka tutorial yang tidak tenggelam dalam detail frontend, dan sejak awal langsung menghasilkan kode assembly yang benar-benar berjalan
Pendekatan yang membangun keseluruhan sistem mulai dari bagian bahasa yang sangat kecil lalu memperluasnya secara bertahap, seperti pendekatan Ghuloum, terasa sangat menarik
Buku bagus yang saya temukan baru-baru ini adalah tautan ini; meskipun C dan x86 bukan target terbaik untuk pemula, itu tetap bahan yang sangat bagus untuk membuat compiler pertama
Sebagai referensi, makalah Ghuloum ada di sini
Dulu parsing adalah bagian yang paling penting sehingga kurikulumnya disusun seperti itu, tetapi sekarang yang lebih penting adalah bagaimana fitur bahasa diimplementasikan
Bahkan kalau membuat compiler sendiri, sekarang kita hidup di zaman ketika Anda bisa berkata, “Claude, buatkan parser recursive descent untuk grammar ini,” dan hasilnya hampir langsung jadi dalam sekali jalan
Kalangan akademis cenderung mendekati masalah dengan basis layer, sedangkan praktisi dengan basis pipe
Pendekatan layer menghasilkan kode yang bisa di-build tetapi sulit dijalankan, sedangkan pendekatan pipe mudah dijalankan tetapi kurang terstruktur
Pada akhirnya, kuncinya adalah membagi pengembangan ke unit minimum yang tetap bisa dijalankan
Menurut saya tulisan ini benar-benar menangkap inti persoalannya dengan sangat baik
Saya sudah tertarik pada compiler sejak sebelum masuk universitas, dan materi ini adalah pengantar yang paling mudah diakses
Saat berusia 17 tahun saya mencoba membuat parser recursive descent sendiri, dan menyadari bahwa masalah yang tampak rumit bisa menjadi sederhana jika dipecah ke primitive dasar yang tepat
Materi tentang algoritma memang banyak, tetapi materi yang membahas bagaimana mendekati masalah lewat primitive masih kurang
Dulu parser berbasis tabel seperti yacc adalah arus utama, tetapi sekarang recursive descent lebih praktis dan fleksibel
Berkat generasi kode oleh LLM, pendekatan ini jadi lebih mudah lagi
Dalam compiler mainan untuk pembelajaran, melewati AST, IR, dan optimisasi lalu langsung ke generasi kode pun tetap sangat bermanfaat
Dulu ketika membuat alat pengujian di perusahaan yang mendukung subset dari C, kami membuat parser mengembalikan struktur data yang bisa dieksekusi alih-alih menghasilkan kode
Misalnya kelas ForLoopStatement berisi testExpression, lalu metode Execute() memanggilnya secara langsung
Makalah ini juga disebut dalam The Mythical Man-Month karya Fred Brooks
Secara konseptual rasanya sangat alami dan bersih
Penjelasan bahwa tutorial Jack Crenshaw menggunakan pendekatan syntax-directed translation terasa menarik
Saya penasaran apakah itu hanya berarti compiler single-pass, atau justru konsep yang lebih spesifik
Saya paham bahwa dalam bahasa bertipe statis optimisasi berbasis tipe itu sulit, tetapi saya ingin tahu apakah ada juga batasan pada tingkat pemeriksaan tipe
Tetapi tanpa IR, optimisasi tingkat lanjut seperti LICM, CSE, dan GVN tidak mungkin dilakukan
Secara pribadi saya lebih suka kompilasi 2-pass per fungsi — pass pertama membuat IR, lalu langsung menghasilkan kode dan membuang statusnya, sehingga hasilnya cepat dan efisien
Compiler single-pass pun bisa saja memisahkan tahap-tahapnya dan baru melakukan generasi kode di bagian akhir
Tutorial Crenshaw memang tidak sampai ke tingkat yang praktis, tetapi kalau diperluas dengan teknik precedence climbing hasilnya jauh lebih berguna
Tulisan terkait dirangkum dengan baik di tautan ini
Saya meninjau lagi thread terkait “Let’s Build a Compiler”
Ada beberapa catatan pernah muncul di HN dari 2007 sampai 2023
Khususnya wawancara Jack Crenshaw, meskipun tanpa komentar, tetap sangat menarik untuk dibaca
Sedikit perkenalan sekaligus promosi proyek pribadi: cwerg.org
Menggunakan Python dan parsing recursive descent, serta memisahkan frontend dan backend lewat IR
Menghasilkan biner ELF untuk x86 dan ARM, dan ditujukan untuk penggunaan nyata
Hanya saja ini cukup kompleks dan bukan bergaya tutorial
Saya masih ingat dulu belajar tutorial Jack Crenshaw dari newsgroup USENET comp.compilers
Sebagai catatan, grup itu masih aktif sampai sekarang
Untuk pendekatan compiler yang lebih modern, saya merekomendasikan tulisan blog Cornell ini
Setiap kali memakainya rasanya seperti menaruh beberapa batu di atas piramida yang sudah ada
Bahasa-bahasa yang memakai LLVM sebagai backend sama-sama punya kecepatan kompilasi yang lambat
Zig menyadari hal ini dan sedang mengembangkan backend sendiri, sementara Rust masih terikat pada kompilasi yang lambat
LLVM memberi ilusi bahwa kompleksitas menjamin kualitas, dan menurut saya ini adalah teknologi yang melemahkan otonomi pengembang
Ini juga mirip dengan teknologi populer lain yang inisialnya kebetulan serupa
Saya juga menulis tutorial bernama tiny-optimizing-compiler karena alasan yang mirip
Ini contoh ringkas yang hanya membahas tahap menengah dan backend compiler
Tautan GitHub
Waktu kecil saya pernah mencetak tutorial ini dan membawanya ke mana-mana untuk dibaca
Melihat compiler selesai dibuat dalam waktu singkat terasa benar-benar keren
Materi seperti ini, seperti Beej’s Guide, adalah salah satu dokumen paling berharga dalam pendidikan CS, tetapi belum mendapatkan apresiasi yang layak