1 poin oleh GN⁺ 2025-12-12 | 1 komentar | Bagikan ke WhatsApp
  • 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.md menjelaskan bagaimana setiap bagian dari versi asli dipetakan ke kode Python
    • Pengguna dapat membaca tutorial aslinya sambil bereksperimen dengan kode yang benar-benar bisa dijalankan
    Iklan

Contoh bahasa KISS dan keluaran WASM

  • Ditampilkan hasil kompilasi program contoh dalam bahasa KISS yang dirancang Crenshaw menggunakan compiler versi Python
    • Contohnya mencakup procedure, loop while, serta pass-by-value dan pass-by-reference
  • Teks WASM yang dihasilkan mencakup logika penanganan parameter referensi (by-reference parameter), dengan hampir tidak ada optimisasi yang diterapkan
  • Bagian di mana variabel global X dikembalikan secara implisit dari fungsi main adalah 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 lex dan yacc dianggap standar, tetapi kesederhanaan dan kejelasan parser buatan tangan memberi pengaruh besar
    • Selama 20 tahun berikutnya, penulis pun menjadi lebih menyukai recursive-descent parser manual
    Iklan
  • 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

 
GN⁺ 2025-12-12
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

    • Ini salah satu kasus yang jarang terjadi di mana menurut saya pendekatan akademis justru tidak selaras dengan kepraktisan
      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
    • Saya merasakan perbedaan antara peneliti akademis dan pengembang praktis
      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

    • “Memecah masalah ke primitive yang tepat” adalah inti sejati dari pemrograman
      Materi tentang algoritma memang banyak, tetapi materi yang membahas bagaimana mendekati masalah lewat primitive masih kurang
    • Recursive descent bukan sekadar cara membuat parser, tetapi juga pelajaran dalam desain program
      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 tahun 1971 karya Niklaus Wirth, Program Development by Stepwise Refinement, adalah karya klasik paling awal yang merumuskan cara berpikir seperti ini
      Makalah ini juga disebut dalam The Mythical Man-Month karya Fred Brooks
    • Sekarang setiap kali perlu parsing, saya cenderung memakai parser combinator
      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

    • Jika bahasa target mengikuti aturan harus didefinisikan sebelum digunakan dan tidak memiliki inferensi tipe yang rumit, maka optimisasi berbasis tipe atau constant folding masih memungkinkan
      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
    • syntax-directed translation adalah konsep yang lebih spesifik, yaitu menghasilkan kode langsung sambil membaca source code
      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

    • Tautan keempat bukan tulisan Crenshaw, melainkan tulisan lain dengan judul yang sama
    • Wawancara Crenshaw benar-benar bagus
  • 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

    • Berkat LLVM, membuat compiler menjadi sangat mengejutkan mudahnya
      Setiap kali memakainya rasanya seperti menaruh beberapa batu di atas piramida yang sudah ada
    • Tetapi LLVM punya keterbatasan karena merupakan pendekatan tidak langsung
      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

    • Sepertinya akan menarik kalau ini diimplementasikan dengan Lean
  • 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