Çok basit bir derleyici nasıl yazılır


214

gccDerleme kodları gibi ileri düzey derleyiciler , kodun yazıldığı dile göre makinede okunabilen dosyalara derler (örn. C, C ++, vb.). Aslında, her bir kodun anlamını kütüphaneye ve karşılık gelen dillerin işlevlerine göre yorumlarlar. Yanlışsam düzelt.

Statik bir dosyayı (örneğin bir metin dosyasında Hello World) derlemek için çok basit bir derleyici (muhtemelen C cinsinden) yazarak derleyicileri daha iyi anlamak istiyorum. Bazı dersler ve kitaplar denedim ama hepsi pratik durumlar için. Dinamik kodları, karşılık gelen dile bağlı anlamlarla derlemekle ilgilenirler.

Statik bir metni makine tarafından okunabilir bir dosyaya dönüştürmek için temel bir derleyiciyi nasıl yazabilirim?

Bir sonraki adım değişkenleri derleyiciye tanıtmak olacaktır; Bir dilin sadece bazı işlevlerini derleyen bir derleyici yazmak istediğimizi hayal edin.

Pratik dersler ve kaynaklar tanıtmak çok takdir edilmektedir :-)



Lex / flex ve yacc / bison denediniz mi?
mouviciel

15
@mouviciel: Bir derleyici oluşturmayı öğrenmenin iyi bir yolu değil. Bu araçlar sizin için zorlu işin önemli bir kısmını yapıyor, bu yüzden aslında hiçbir zaman yapmıyor ve nasıl yapıldığını öğreniyorsunuz.
Mason Wheeler

11
İlginçtir @Mat, linklerinizin ilk 404 verir, ikincisi ise şimdi bu sorunun bir kopyası olarak işaretlendi .
Ruslan,

Yanıtlar:


326

giriş

Tipik bir derleyici aşağıdaki adımları gerçekleştirir:

  • Ayrıştırma: kaynak metin, soyut bir sözdizimi ağacına (AST) dönüştürülür.
  • Referansların diğer modüllere çözümlenmesi (C, bu adımı bağlantı verene kadar erteler).
  • Anlamsal doğrulama: ulaşılamayan kod veya yinelenen beyannameler gibi hiçbir anlam ifade etmeyen sözdizimsel olarak doğru beyanları ayıklamak.
  • Eşdeğer dönüşümler ve üst düzey optimizasyon: AST, aynı semantik ile daha verimli bir hesaplamayı temsil edecek şekilde dönüştürülür. Bu, örneğin, aşırı yerel atamaları ortadan kaldırarak (ayrıca SSA'ya bakınız ) vb . Ortak alt ifadelerin ve sabit ifadelerin erken hesaplanmasını içerir .
  • Kod oluşturma: AST, atlamalar, kayıt tahsisi ve benzerleriyle lineer düşük seviye koda dönüştürülür. Bazı işlev çağrıları bu aşamada, bazı döngüler kontrolsüz vb.
  • Gözetleme deliği optimizasyonu: Düşük seviyeli kod, ortadan kaldırılan basit yerel verimsizliklere karşı taranır.

Modern derleyicilerin çoğu (örneğin, gcc ve clang) son iki adımı bir kez daha tekrarlar. İlk kod oluşturma için orta seviye düşük ama platformdan bağımsız bir dil kullanıyorlar. Daha sonra bu dil, platforma özgü bir şekilde aşağı yukarı aynı şeyi yaparak platforma özgü bir koda (x86, ARM vb.) Dönüştürülür. Bu, örneğin mümkünse vektör talimatlarının kullanımını, dal tahmin verimliliğini arttırmak için talimatların yeniden düzenlenmesini vb.

Bundan sonra, nesne kodu bağlantı için hazırdır. Yerel kod derleyicilerinin çoğu, yürütülebilir bir dosya üretmek için bir bağlayıcıyı nasıl arayacaklarını bilir, ancak bu bir derleme adımı değildir. Java ve C # gibi dillerde, VM tarafından yükleme sırasında yapılan tamamen dinamik olabilir.

Temelleri hatırla

  • Çalışmasını sağla
  • Güzel yapmak
  • Verimli yapmak

Bu klasik dizi, tüm yazılım geliştirme için geçerlidir, ancak tekrarlama yapar.

Dizinin ilk basamağına konsantre olun. İşe yarayabilecek en basit şeyi yarat.

Kitapları oku!

Aho ve Ullman'ın Dragon Kitabı'nı okuyun . Bu klasik ve bugün hala oldukça uygulanabilir.

Modern Derleyici Tasarımı da övgüyle karşılandı.

Bu şey şu anda sizin için çok zorsa, önce ayrıştırma hakkında bazı tanıtımları okuyun; genellikle kütüphaneleri ayrıştırma intros ve örnekler içerir.

Özellikle ağaçlarla grafiklerle çalışırken rahat olduğunuzdan emin olun. Bu şeyler, programların mantıksal düzeyde yapıldığı şeylerdir.

Dilinizi iyi tanımlayın

İstediğiniz gösterimi kullanın, ancak dilinizin tam ve tutarlı bir tanımına sahip olduğunuzdan emin olun. Bu, hem sözdizimi hem de anlambilimi içerir.

Gelecekteki derleyici için test senaryoları olarak yeni dilinizde kod parçacıkları yazma zamanı geldi.

Favori dilini kullan

Python veya Ruby'de bir derleyici yazmak veya sizin için hangi dilde olursa olsun tamamen sorun değil. İyi anladığınız basit algoritmalar kullanın. İlk sürümün hızlı, verimli veya özellik tamamlayıcı olması gerekmez. Sadece yeterince doğru ve değiştirilmesi kolay olması gerekiyor.

Gerektiğinde derleyicinin farklı aşamalarını farklı dillerde yazmak da sorun değil.

Çok fazla test yazmaya hazırlanın

Tüm diliniz test durumlarıyla örtülmeli; etkili bir şekilde onlar tarafından tanımlanacaktır . Tercih ettiğiniz test çerçevesiyle tanışın. İlk günden itibaren testleri yazın. Yanlış kod tespiti yerine, doğru kodu kabul eden 'pozitif' testlere yoğunlaşın.

Tüm testleri düzenli olarak yapın. Devam etmeden önce kırık testleri düzeltin. Geçerli bir kodu kabul edemeyen kötü tanımlanmış bir dille bitmek utanç verici olur.

İyi bir ayrıştırıcı oluşturun

Ayrıştırıcı jeneratörler çoktur . Ne istersen seç. Ayrıca sıfırdan kendi ayrıştırıcı yazmak olabilir, ama bu sadece değer senin dilin sözdizimi ise ölü basit.

Ayrıştırıcı sözdizimi hatalarını algılamalı ve raporlamalıdır. Hem olumlu hem de olumsuz birçok test vakası yazın; Dili tanımlarken yazdığınız kodu tekrar kullanın.

Ayrıştırıcınızın çıktısı soyut bir sözdizimi ağacıdır.

Dilinizde modüller varsa, çözümleyicinin çıktısı, oluşturduğunuz 'nesne kodunun' en basit gösterimi olabilir. Bir ağacı bir dosyaya dökmek ve hızlı bir şekilde geri yüklemek için birçok basit yol vardır.

Anlamsal bir doğrulayıcı oluşturun

Büyük olasılıkla diliniz, belirli bağlamlarda anlam ifade etmeyen sözdizimsel olarak doğru yapılara izin verir. Bir örnek, aynı değişkenin yinelenen bir bildirimidir veya yanlış türde bir parametreyi iletmektedir. Doğrulayıcı, ağaca bakarak bu tür hataları algılar.

Doğrulayıcı, kendi dilinizde yazılmış diğer modüllere referansları da çözecek, bu diğer modülleri yükleyecek ve doğrulama işleminde kullanacaktır. Örneğin, bu adım başka bir modülden bir işleve geçirilen parametre sayısının doğru olduğundan emin olacaktır.

Yine birçok test senaryosu yazıp çalıştırın. Önemsiz durumlar, akıllı ve karmaşık sorun giderme işlemlerinde vazgeçilmezdir.

Kodunu oluşturun

Bildiğiniz en basit teknikleri kullanın. Genellikle, bir dil yapısını (bir ififade gibi ) bir HTML şablonundan farklı olarak, hafif parametreli bir kod şablonuna doğrudan çevirmek uygundur .

Yine, verimliliği yok sayın ve doğruluğa odaklanın.

Platformdan bağımsız, düşük seviyeli bir VM'yi hedefleyin

Donanıma özgü ayrıntılara özellikle ilgi duymadığınız sürece düşük seviyeli şeyleri görmezden geldiğinizi varsayalım. Bu ayrıntılar kanlı ve karmaşıktır.

Seçenekleriniz:

  • LLVM: genellikle x86 ve ARM için verimli makine kodu oluşturulmasına izin verir.
  • CLR: .NET, çoğunlukla x86 / Windows tabanlı; JIT’i iyi.
  • JVM: Java dünyasını hedef alıyor, çok platformlu, iyi bir JIT'ye sahip.

Optimizasyonu yoksay

Optimizasyon zor. Neredeyse her zaman optimizasyon erkendir. Verimsiz ama doğru kod oluşturun. Ortaya çıkan kodu optimize etmeye çalışmadan önce tüm dili uygulayın.

Tabii ki, önemsiz optimizasyonlar tanıtmak için tamam. Ancak derleyiciniz kararlı olmadan önce kurnaz, tüylü şeylerden kaçının.

Ne olmuş yani?

Bütün bunlar senin için çok korkutucu değilse, lütfen devam et! Basit bir dil için, adımların her biri düşündüğünüzden daha basit olabilir.

Derleyicinizin oluşturduğu bir programdan 'Merhaba dünyayı' görmek çabaya değer olabilir.


45
Bu, şu ana kadar gördüğüm en iyi cevaplardan biri.
gahooa

11
Sanırım sorunun bir bölümünü kaçırdın ... OP çok basit bir derleyici yazmak istedi . Bence burada çok temellerin ötesine geçersiniz.
marco-fiset 21.01

22
@ marco-fiset , aksine, OP'ye çok temel bir derleyicinin nasıl yapılacağını söyleyen ve daha ileri aşamaları önlemek ve tanımlamak için tuzaklara işaret eden olağanüstü bir cevap olduğunu düşünüyorum.
smci

6
Bu, Stack Exchange evreninin tamamında gördüğüm en iyi cevaplardan biri. Kudos!
Andre Terra

3
Derleyicinizin oluşturduğu bir programdan 'Merhaba dünyayı' görmek çabaya değer olabilir. - INDEED
56'da

27

Jack Crenshaw'ın Let's Compiler'i Derleyelim, bitmemiş olsa da, okunaklı bir giriş ve öğretici.

Nicklaus Wirth'in Derleyici Yapısı , basit derleyici yapılarıyla ilgili çok iyi bir kitaptır. Yukarıdan aşağıya özyinelemeli inişe odaklanıyor, ki bununla yüzleşelim, lex / yacc veya flex / bison'dan daha kolay bir LOT. Grubunun yazdığı orijinal PASCAL derleyicisi bu şekilde yapıldı.

Diğer insanlar çeşitli Ejderha kitaplarından bahsetti.


1
Pascal hakkındaki güzel şeylerden biri, kullanılmadan önce her şeyin tanımlanması veya bildirilmesi gerektiğidir. Bu nedenle tek geçişte derlenebilir. Turbo Pascal 3.0 buna bir örnektir ve buradaki internaller hakkında çok fazla belge var .
tcrosley

1
PASCAL, tek geçişli derleme ve bağlantı düşünülerek özel olarak tasarlanmıştır. Wirth'in derleyici kitabı çoklu derleyicilerden bahseder ve 70 (evet, yetmiş) geçişi alan bir PL / I derleyicisini bildiğini ekler.
John R. Strohm

Kullanımdan önce zorunlu bildirim, ALGOL'e dayanır. Tony Hoare, FORTRAN'ınkine benzer varsayılan kurallar eklemeyi önermeye çalıştığında ALGOL komitesi tarafından kulaklarını sıktı. Bunun yaratabileceği sorunları zaten biliyorlardı; isimlerdeki tipografik hatalar ve ilginç hatalar yaratan varsayılan kurallar.
John R. Strohm

1
: Burada asıl yazarın kendisi tarafından kitabın bir daha güncel ve bitmiş sürümüdür stack.nl/~marcov/compiler.pdf bu :) cevap düzenleyip ekleyin
sone

16

Aslında Brainfuck için bir derleyici yazmaya başlıyorum . Programlanması oldukça geniş bir dildir ancak uygulanması gereken sadece 8 talimatı vardır. Bu, elde edebileceğiniz kadar basittir ve sözdizimi ertelemeyi bulursanız, söz konusu komutlar için eşdeğer C yönergeleri vardır.


7
Ancak, BF derleyicinizi hazır duruma getirdikten sonra, kodunuzu içine yazmanız gerekir :(
500 - Dahili Sunucu Hatası,

@ 500-InternalServerError, C alt küme yöntemini kullanır
World Engineer

12

Yalnızca makineyle okunabilen bir kod yazmak istiyorsanız ve sanal bir makineyi hedeflemiyorsanız, Intel kılavuzlarını okuyup anlamanız gerekir.

  • a. Yürütülebilir kodun bağlanması ve yüklenmesi

  • b. COFF ve PE formatları (pencereler için), alternatif olarak ELF formatını anlayın (Linux için)

  • c. .COM dosya formatlarını anlama (PE'den daha kolay)
  • d. Montajcıları anlamak
  • e. Derleyicileri ve derleyicilerdeki kod oluşturma motorunu anlayın.

Söylenenden çok daha zor. Derleyicileri ve tercümanları C ++ 'da başlangıç ​​noktası olarak okumanızı öneririm (Yazan Ronald Mak). Alternatif olarak, "derleyici yapalım" Crenshaw tarafından TAMAM.

Bunu yapmak istemiyorsanız, kendi VM'nizi de yazabilir ve bu VM'yi hedefleyen bir kod oluşturucu yazabilirsiniz.

İpuçları: Flex ve Bison İLK öğrenin. Sonra kendi derleyicinizi / VM'nizi oluşturun.

İyi şanslar!


7
LLVM'yi hedef almanın ve gerçek makine kodunun bulunmamasının bugün mevcut en iyi yöntem olduğunu düşünüyorum.
9000

Kabul ediyorum, bir süredir LLVM'yi izliyorum ve bunu hedeflemek için gereken programcı çabası açısından yıllarca gördüğüm en iyi şeylerden biri olduğunu söylemeliyim!
Aniket Inge,

2
Peki ya MIPS ve çalıştırmak için spim kullanın? Veya MIX ?

@MichaelT MIPS kullanmadım ama iyi olacağından eminim.
Aniket Inge,

@PrototypeStark RISC komut seti, bugün hala kullanımda olan gerçek dünya işlemcisi (bunun gömülü sistemlere çevrilebileceğini anlamak). Tam komut seti wikipedia'da . İnternete baktığımızda, pek çok örnek var ve birçok akademik sınıfta makine dili programlaması için bir hedef olarak kullanılıyor. Üzerinde aktivitenin biraz az olduğunu SO .

10

Basit derleyici için DIY yaklaşımı şöyle görünebilir (en azından benim üniversitedeki projem böyle görünüyordu):

  1. Dilin gramerini tanımlar. Bağlam muaf.
  2. Dilbilginiz henüz LL (1) değilse, şimdi yapın. Düz CF gramerinde iyi görünen bazı kuralların çirkin gelebileceğini unutmayın. Belki de diliniz çok karmaşık ...
  3. Metin akışını belirteçlere (kelimeler, sayılar, değişmezler) kesen Lexer yazın.
  4. Dilbilginiz için, girişi kabul eden veya reddeden yukarıdan aşağıya özyinelemeli iniş ayrıştırıcı yazın.
  5. Ayrıştırıcınıza sözdizimi ağacı oluşturma işlemini ekleyin.
  6. Makine kodu üretecini sözdizimi ağacından yazın.
  7. Kar & Bira, alternatif olarak daha akıllı ayrıştırıcı yapmayı veya daha iyi kod oluşturmayı düşünmeye başlayabilirsiniz.

Her adımı ayrıntılı olarak anlatan bol miktarda literatür bulunmalıdır.


7. nokta, OP'nin sorduğu şey.
Florian Margaine

7
1-5 ilgisizdir ve bu kadar yakın bir ilgiyi hak etmemektedir. 6 en ilginç kısımdır. Ne yazık ki, kitapların çoğu, aynı ejderhayı takip ediyor, rezil ejderha kitabından sonra, kod ayrıştırma ve kod dışı bırakma işlemlerine çok fazla dikkat ederek.
SK-mantığı
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.