Let’s Build a Compiler’a Yeniden Bakış
(eli.thegreenplace.net)- 1988~1995 yılları arasında yayımlanan Jack Crenshaw'ın ‘Let’s Build a Compiler’ kılavuzunu Python ve WebAssembly ile yeniden uygulayan bir proje
- Orijinalde Pascal ve Motorola 68000 assembly kullanılmıştı, ancak bu çalışmada modern bir ortamda çalışabilir koda dönüştürüldü
- Kılavuzun odak noktası, özyinelemeli iniş ayrıştırıcısını (recursive-descent parser) doğrudan uygulamak ve ilk aşamalardan itibaren gerçek assembly kodu üretmeye geçmek oldu
- Crenshaw’nin yaklaşımı söz dizimi yönlendirmeli çeviri (syntax-directed translation) kullanıyor, ancak tip işleme aşamasında sınırlılıklarını gösteriyor
- Bu yeniden uygulama, klasik kılavuzu modern geliştiricilerin doğrudan çalıştırıp deneyebileceği bir biçimde geri getirmesi açısından önem taşıyor
Klasik kılavuzun arka planı
- Jack Crenshaw'ın ‘Let’s Build a Compiler’ 1988~1995 yılları arasında yayımlanan, bugün hâlâ sıkça anılan bir derleyici yapım eğitim kaynağıdır
- Orijinal metin Pascal ile yazılmıştı ve Motorola 68000 assembly kodu üretirdi
- 35 yıl sonra, 2025'te bile Hacker News gibi platformlarda düzenli olarak gündeme geliyor
- Yazar bu kılavuzla 2003'te ilk kez karşılaştığında derin bir etki aldığını ve 2025'te onu Python ile WebAssembly'e yeniden taşıdığını anlatıyor
Python ve WebAssembly ile yeniden uygulama
- Sadece okumakla kalmayıp, kılavuzun derleyicisini Python'a çevirip WebAssembly (WASM) üretecek şekilde uygulanmış
- Çıktı, GitHub deposunda (eliben/letsbuildacompiler) yayınlanıyor
TUTORIAL.mddosyasında her bölümün orijinal metinden Python koduna nasıl eşlendiği açıklanıyor- Kullanıcılar, orijinal kılavuzu okurken doğrudan çalıştırılabilir kodlar ile deney yapabiliyor
KISS dili örneği ve WASM çıktısı
- Crenshaw tarafından tasarlanan KISS dilinin örnek programı, Python sürümü derleyici ile derlenmiş hâliyle gösteriliyor
- Örnek,
procedure,whiledöngüsü ve değer iletimi ile referans iletimini içeriyor
- Örnek,
- Üretilen WASM metni, başvuru parametresi (by-reference parameter) işleme mantığını içeriyor ve optimizasyon neredeyse hiç uygulanmamış
mainfonksiyonunda global değişkenXin örtülü olarak döndürülmesi, test kolaylığı için eklenmiş bir düzenektir- Üretilen WASM kodu, gerçek çalıştırmayla beklenen sonucu doğrulamak için testlerde kullanılıyor
Kılavuzun güçlü yönleri
- Crenshaw’nin kılavuzu özyinelemeli iniş ayrıştırıcısını adım adım inşa ederken, karmaşık teoriye göre doğrudan çalışan kod üretimine öncelik verir
- O dönemde
lexveyaccstandart araçlar olarak görülmesine rağmen, el ile yazılan ayrıştırıcının sadeliği ve açıklığı güçlü bir etki bırakır - Yazar daha sonraki 20 yıl boyunca manuel özyinelemeli iniş ayrıştırıcısını tercih etmeye başlar
- O dönemde
- Ayrıca çoğu dersin yalnızca sözdizimi çözümlemeye odaklandığı bir dönemde Crenshaw, başlangıç aşamalarından itibaren assembly kod üretimini ele alır
Sınırlamalar ve geliştirme potansiyeli
- Kılavuz, ayrıştırma sırasında doğrudan kod üretmek için söz dizimi yönlendirmeli çeviri (syntax-directed translation) yaklaşımını kullanır
- Bu yöntem eğitici olarak faydalı olsa da, önceden tür bilgisinin bilinmemesinden dolayı optimizasyonun zor olduğu bir sınıra takılır
- Özellikle türlerin 14. bölümde tanıtılmasından sonra orta seviye temsil (IR) veya AST kullanan yapısal bir yaklaşımın gerekliliği ortaya çıkar
- Crenshaw'nin 14. bölümden sonra kılavuza ara vermesinin nedeni belirtilmemiş olsa da, bunun bu sınırlamayla ilgili olabileceği öne sürülüyor
- Yazar, 14. bölümü bir dönüm noktası kabul ederek, tür denetimi ve kod üretiminin AST üretildikten sonra yapılmasının daha uygun olduğu görüşünde
Sonuç
- Orijinal kılavuz, hâlâ bir derleyici yapım giriş kitabı olarak yüksek okunabilirlik ve pratiklik sunuyor
- Bu Python·WASM sürümü, modern geliştiricilerin eski teknolojilere saplanmadan öğrenebilmeleri için tamamlayıcı bir uygulama olarak sunuluyor
- GitHub deposu sayesinde herkesin deney yapabilmesi mümkün; derleyici mimarisini doğrudan deneyimleme şansı veren modern bir yeniden yorum olarak değerlendiriliyor
1 yorum
Hacker News görüşleri
Frontend ayrıntılarında kaybolmadan, en baştan itibaren doğrudan çalışan assembly kodu üreten bir öğreticiyi beğendim
Ghuloum’un yaklaşımında olduğu gibi dilin çok küçük bir parçasından başlayıp tüm yapıyı adım adım genişleten yöntem özellikle çekici
Yakın zamanda keşfettiğim iyi bir kitap da bu bağlantıdaki kitap; C ve x86 yeni başlayanlar için en ideal hedefler olmasa da ilk derleyiciyi yapmak için harika bir kaynaktı
Bu arada Ghuloum’un makalesi burada
Eskiden parsing en önemli kısımdı, bu yüzden müfredat da öyle şekillenmişti; bugünse dil özelliklerinin nasıl uygulanacağı daha önemli
Kendi derleyicini yazıyor olsan bile artık “Claude, bana bu gramer için bir recursive descent parser yaz” dediğinde çoğu zaman tek seferde sonuç alabildiğin bir dönemdeyiz
Akademi katman (layer) temelli, uygulamacılar ise boru hattı (pipe) temelli yaklaşma eğiliminde
Katman yaklaşımı derlenebilir kod verir ama çalıştırması zordur; boru hattı yaklaşımı ise çalıştırmayı kolaylaştırır ama yapısallığı zayıftır
Sonuçta asıl mesele, geliştirmeyi çalıştırılabilir en küçük birimlere bölmektir
Bence bu yazı gerçekten meselenin özünü çok iyi yakalamış
Üniversiteye gitmeden önce bile derleyicilere ilgim vardı ve bu kaynak karşılaştığım en erişilebilir giriş materyaliydi
17 yaşındayken recursive descent parser’ı kendim yazarak, karmaşık görünen sorunların doğru primitive’lere bölündüğünde basitleştiğini fark etmiştim
Algoritmalar hakkında çok kaynak var ama problemlere primitive düzeyinde nasıl yaklaşılacağını anlatan kaynak az
Eskiden yacc gibi tablo tabanlı parser’lar baskındı ama bugün recursive descent daha pratik ve esnek
LLM ile kod üretimi sayesinde bu yaklaşım daha da kolaylaştı
Öğrenme amaçlı oyuncak derleyicilerde AST, IR veya optimizasyonu atlayıp doğrudan kod üretimine geçmek bile fazlasıyla öğretici olabilir
Eskiden şirkette C’nin bir alt kümesini destekleyen bir test aracı yaparken, parser’ın kod üretmek yerine çalıştırılabilir veri yapıları döndürmesini sağlamıştık
Örneğin ForLoopStatement sınıfı bir testExpression içeriyor ve
Execute()metodu bunu doğrudan çağırıyorduFred Brooks’un The Mythical Man-Month kitabında da anılan önemli bir makale
Kavramsal olarak çok doğal ve temiz geliyor
Jack Crenshaw’ın öğreticisinde syntax-directed translation yaklaşımının kullanıldığı açıklaması ilginç geldi
Bunun yalnızca single-pass compiler anlamına mı geldiğini, yoksa daha özel bir kavram mı olduğunu merak ediyorum
Statik tipli dillerde tür tabanlı optimizasyonun zor olduğunu anlıyorum ama tür denetimi düzeyinde de kısıtlar olup olmadığını bilmek isterim
Ama IR yoksa LICM, CSE, GVN gibi ileri seviye optimizasyonlar yapılamaz
Ben şahsen fonksiyon bazında 2 geçişli derlemeyi tercih ediyorum — ilk geçişte IR oluşturup, hemen ardından kod üretip durumu atarak hızlı ve verimli sonuç alınabiliyor
Single-pass compiler olsa bile aşamalar ayrılıp kod üretimi sadece en sonda da yapılabilir
Crenshaw’ın öğreticisi pratik düzeye kadar gitmese de, precedence climbing tekniğiyle genişletilirse çok daha kullanışlı hale geliyor
İlgili yazı bu bağlantıda güzelce anlatılmış
“Let’s Build a Compiler” ile ilgili başlıklara tekrar göz attım
2007’den 2023’e kadar HN’de birkaç kez paylaşıldığına dair kayıtlar var
Özellikle Jack Crenshaw röportajı yorum içermese de çok ilginç bir okumaydı
Kendi projem için küçük bir tanıtım da yapayım: cwerg.org
Python ve recursive descent parsing kullanıyor, frontend ile backend’i IR üzerinden ayırıyor
x86 ve ARM için ELF ikilileri üretiyor ve gerçek kullanım hedefleniyor
Ama epey karmaşık; öğretici tarzda bir şey değil
Eskiden USENET’teki comp.compilers haber grubunda Jack Crenshaw’ın öğreticisini öğrenmiş olmanın nostaljisini yaşadım
Bu arada o grup hâlâ aktif
Modern bir derleyici yaklaşımı için şu Cornell blog yazısını öneririm
Her kullandığımda kendimi sanki bir piramidin üstüne birkaç taş daha koyuyormuşum gibi hissediyorum
LLVM’i backend olarak kullanan dillerde ortak nokta yavaş derleme hızları
Zig bunun farkında ve kendi backend’ini geliştiriyor; Rust ise hâlâ yavaş derleme sürelerine bağlı
LLVM, karmaşıklığın kaliteyi garanti ettiği yanılsamasını veriyor ve geliştiricinin özerkliğini zayıflatan bir teknoloji gibi geliyor
Bu, popüler başka bazı teknolojilere de benziyor (baş harfleri de benzer)
Ben de benzer nedenlerle tiny-optimizing-compiler adlı bir öğretici yazdım
Derleyicinin yalnızca ara aşamaları ve backend kısmını ele alan kısa bir örnek
GitHub bağlantısı
Küçükken bu öğreticiyi çıktısını alıp yanımda taşıyarak okurdum
Kısa sürede bir derleyicinin tamamlandığını görmek gerçekten harikaydı
Beej’s Guide gibi bu tür materyaller CS eğitimindeki en değerli belgelerden bazıları ama hak ettikleri değeri görmüyorlar