Bir Programlama Dili Nasıl Çalışır?
Bu yazıda bir programlama dilinin kaynak koddan başlayıp işlemci üzerinde çalışan programa dönüşmesine kadar geçen süreci adım adım inceleyeceğiz.
Bir geliştirici ekrana birkaç satır kod yazdığında bilgisayar bu kodu doğrudan anlamaz. Python, JavaScript, C#, Java, C veya Rust gibi programlama dilleri insanların okuyabileceği şekilde tasarlanmıştır. İşlemcinin anlayabildiği şey ise çok daha düşük seviyeli makine komutlarıdır.
Bu nedenle yazdığımız kaynak kodun bilgisayar tarafından çalıştırılabilmesi için arada birçok aşama gerçekleşir. Kod önce analiz edilir, anlamlandırılır, gerektiğinde başka bir ara dile dönüştürülür ve sonunda işlemcinin çalıştırabileceği komutlara çevrilir.
Bu yazıda bir programlama dilinin kaynak koddan başlayıp işlemci üzerinde çalışan programa dönüşmesine kadar geçen süreci adım adım inceleyeceğiz.
Bilgisayar Programlama Dilini Doğrudan Anlar mı?
Hayır. İşlemci Python, C#, JavaScript veya C++ gibi programlama dillerini doğrudan anlamaz.
Modern işlemciler kendi instruction set adı verilen komut kümeleriyle çalışır. x86-64 ve ARM64 bunun en bilinen örnekleridir.
Bir işlemci açısından program sonunda toplama yapmak, bellekteki bir değeri okumak, başka bir adrese atlamak veya register içerisindeki veriyi değiştirmek gibi düşük seviyeli komutlardan oluşur.
Programlama dilleri ise bu karmaşıklığın üzerine insan tarafından anlaşılabilen bir soyutlama katmanı ekler.
Kaynak Kod Nedir?
Bir geliştiricinin programlama dili kullanarak yazdığı metne kaynak kod veya source code adı verilir.
Örneğin TypeScript ile iki sayıyı toplayan oldukça basit bir fonksiyon yazabiliriz.
function topla(a: number, b: number): number {
return a + b;
}
const sonuc = topla(10, 20);Bir insan bu kodu okuyarak iki sayının toplandığını kolayca anlayabilir. Ancak işlemci function, return veya number gibi ifadelerin ne anlama geldiğini bilmez.
Kodun çalışabilmesi için programlama dilinin araçları tarafından işlenmesi gerekir.
Bir Programın Çalışma Süreci Genel Olarak Nasıldır?
- Geliştirici kaynak kodu yazar.
- Kod lexical analysis aşamasından geçirilir.
- Parser kodun yapısını analiz eder.
- Kodun anlamı ve türleri kontrol edilir.
- Ara temsil oluşturulabilir.
- Kod optimize edilebilir.
- Makine kodu veya bytecode oluşturulur.
- İşletim sistemi programı belleğe yükler.
- İşlemci komutları çalıştırır.
Her programlama dili bu aşamaların tamamını aynı şekilde uygulamaz. Ancak modern compiler ve runtime sistemlerinin büyük bölümünde benzer prensipler bulunur.
1. Lexical Analysis: Kod Önce Parçalara Ayrılır
Compiler veya interpreter kaynak kodu ilk olarak anlamlı küçük parçalara ayırır. Bu aşamaya lexical analysis, işlemi gerçekleştiren bileşene ise lexer veya tokenizer denir.
Örneğin aşağıdaki ifade düşünülebilir.
const toplam = 10 + 20;Lexer bu kodu yaklaşık olarak şu tür parçalara ayırabilir.
- const anahtar kelimesi
- toplam değişken adı
- eşittir operatörü
- 10 sayısal değeri
- artı operatörü
- 20 sayısal değeri
Bu küçük yapılara token adı verilir.
Programın geri kalan analiz aşamaları doğrudan ham karakterlerle uğraşmak yerine bu token yapıları üzerinden ilerleyebilir.
2. Parsing: Kodun Yapısı Anlaşılır
Tokenlara ayrılan kodun yalnızca hangi kelimelerden oluştuğunu bilmek yeterli değildir. Bu parçaların birbirleriyle nasıl ilişkili olduğunun da anlaşılması gerekir.
Bu görevi parser gerçekleştirir.
Parser programlama dilinin grammar adı verilen sözdizimi kurallarını kullanarak kodun geçerli olup olmadığını kontrol eder.
Örneğin aşağıdaki kod sözdizimi açısından hatalı olabilir.
const toplam = + 10 20;Programlama dilinin grammar kuralları böyle bir ifadeye izin vermiyorsa parser bu noktada hata üretir.
Abstract Syntax Tree Nedir?
Parser çoğu compiler sisteminde kaynak kodu Abstract Syntax Tree adı verilen ağaç yapısına dönüştürür. Kısaca AST olarak adlandırılır.
AST kodun metinsel görünümünden çok anlamlı yapısını temsil eder.
Örneğin 10 + 20 işlemi bir toplama düğümü ve onun altında bulunan iki sayısal değer şeklinde temsil edilebilir.
- Program
- Değişken tanımı
- Toplama işlemi
- 10 değeri
- 20 değeri
Compiler kod üzerinde birçok işlemi bu AST yapısını kullanarak gerçekleştirir.
3. Semantic Analysis: Kod Mantıksal Olarak Doğru mu?
Bir kod sözdizimi açısından doğru olabilir ancak anlam açısından hatalı olabilir.
Örneğin bir fonksiyona sayı verilmesi gerekirken tamamen farklı bir veri türü gönderilmiş olabilir veya kod içerisinde tanımlanmamış bir değişken kullanılabilir.
Semantic analysis aşamasında compiler bu tür problemleri kontrol eder.
- Değişken tanımlanmış mı?
- Fonksiyon gerçekten var mı?
- Fonksiyona doğru parametreler verilmiş mi?
- Veri türleri birbiriyle uyumlu mu?
- Bir değer geçerli kapsam içerisinde mi?
Özellikle TypeScript, C#, Java, Rust ve C++ gibi güçlü tip sistemlerine sahip dillerde bu aşama oldukça önemlidir.
Type System Ne İşe Yarar?
Type system program içerisindeki verilerin hangi türde olduğunu takip eder.
Bir değişken sayı mı, metin mi, boolean değer mi, bir nesne mi veya başka bir veri yapısı mı?
Compiler bu bilgileri kullanarak bazı hataların program çalışmadan önce tespit edilmesini sağlayabilir.
const kullaniciAdi: string = 50;Bu örnekte kullaniciAdi değişkeninin string olması gerektiği belirtilmesine rağmen sayı verilmiştir. TypeScript bu problemi çalışma zamanına ulaşmadan önce tespit edebilir.
4. Intermediate Representation Nedir?
Modern compiler sistemleri kaynak kodu çoğu zaman doğrudan makine koduna çevirmek yerine önce Intermediate Representation adı verilen bir ara temsile dönüştürür.
Kısaca IR adı verilen bu yapı programlama dili ile fiziksel işlemci arasında bir ara katman oluşturur.
Bu yaklaşım compiler geliştiricilerine önemli avantajlar sağlar.
- Kod üzerinde optimizasyon yapmak kolaylaşır.
- Farklı işlemci mimarileri hedeflenebilir.
- Aynı backend birden fazla programlama dili tarafından kullanılabilir.
- Compiler mimarisi daha modüler hale gelir.
LLVM bu yaklaşımın en bilinen örneklerinden biridir ve birçok modern programlama dili tarafından compiler altyapısı içerisinde kullanılmaktadır.
5. Compiler Kodu Optimize Eder
Compiler'ın görevi yalnızca kaynak kodu başka bir dile çevirmek değildir. Modern compiler sistemleri programı daha verimli çalıştırabilmek için çok sayıda optimizasyon gerçekleştirebilir.
Örneğin geliştiricinin yazdığı bazı işlemlerin sonucunun daha program çalışmadan hesaplanması mümkün olabilir.
const sonuc = 10 * 20;Compiler bazı durumlarda bu işlemi çalışma zamanında tekrar hesaplamak yerine doğrudan 200 değerine dönüştürebilir.
Bu tür optimizasyonlardan biri constant folding olarak adlandırılır.
Compiler Başka Hangi Optimizasyonları Yapabilir?
- Kullanılmayan kodları kaldırabilir.
- Fonksiyonları inline hale getirebilir.
- Döngüleri optimize edebilir.
- Gereksiz bellek erişimlerini azaltabilir.
- İşlemci register kullanımını optimize edebilir.
- Sabit ifadeleri önceden hesaplayabilir.
Bu nedenle aynı algoritmanın farklı compiler ve optimizasyon ayarlarıyla oluşturulan sürümleri farklı performans gösterebilir.
6. Machine Code Oluşturulur
Native olarak derlenen C, C++, Rust ve Zig gibi dillerde compiler işlemin sonunda hedef işlemci mimarisine uygun makine komutları oluşturabilir.
Bu aşamada artık geliştiricinin yazdığı değişken isimleri veya fonksiyon sözdizimi işlemci açısından önemli değildir. Program işlemcinin instruction set içerisinde bulunan gerçek komutlara dönüştürülmüştür.
Örneğin yüksek seviyede çok basit görünen bir toplama işlemi düşük seviyede registerlara veri yükleme, toplama ve sonucu belleğe yazma gibi birkaç ayrı işlemden oluşabilir.
Assembly Nedir?
Assembly, makine koduna oldukça yakın ancak insanlar tarafından okunabilmesi için sembolik isimler kullanan düşük seviyeli bir programlama dilidir.
Örneğin işlemcide toplama işlemi gerçekleştiren binary komutları doğrudan okumak yerine assembly içerisinde ADD gibi komut isimleri görülebilir.
Compiler tarafından oluşturulan makine kodunu anlamak veya performans analizi yapmak isteyen geliştiriciler zaman zaman programın assembly çıktısını inceleyebilir.
Compiler Nedir?
Compiler, kaynak kodu başka bir forma dönüştüren yazılımdır.
Bu dönüşüm doğrudan makine koduna yapılabileceği gibi başka bir ara dile veya bytecode yapısına da yapılabilir.
C, C++, Rust ve Zig gibi diller çoğunlukla native executable üretme modeliyle ilişkilendirilir.
Ancak modern programlama dillerini yalnızca “derlenen” ve “yorumlanan” şeklinde iki kesin gruba ayırmak her zaman doğru değildir. Bir dilin farklı implementasyonları farklı çalışma modelleri kullanabilir.
Interpreter Nedir?
Interpreter kaynak kodu veya daha önce oluşturulmuş ara temsili çalışma zamanında değerlendirerek programı çalıştıran sistemdir.
Klasik compiler yaklaşımında uygulamanın büyük bölümü program çalıştırılmadan önce dönüştürülürken interpreter tabanlı sistemlerde bazı işlemler çalışma zamanında gerçekleştirilir.
Python bu modele verilebilecek en bilinen örneklerden biridir. Ancak Python'ın çalışma süreci de kaynak kodun doğrudan satır satır işlemciye gönderilmesinden daha karmaşıktır.
Python Nasıl Çalışır?
Python için sıkça “yorumlanan bir dil” ifadesi kullanılır. Ancak yaygın Python implementasyonu CPython kaynak kodu önce bytecode adı verilen bir ara forma dönüştürür.
Bu bytecode daha sonra Python Virtual Machine tarafından çalıştırılır.
- Python kaynak kodu yazılır.
- Kod parse edilir.
- Bytecode oluşturulur.
- Python Virtual Machine bytecode komutlarını çalıştırır.
Dolayısıyla “Python hiçbir şekilde derlenmez” demek teknik olarak doğru değildir. Kaynak kod doğrudan native executable haline getirilmese de çalışma sürecinde bir derleme aşaması bulunabilir.
Java Nasıl Çalışır?
Java farklı bir yaklaşım kullanır. Java kaynak kodu doğrudan belirli bir işlemci mimarisine çevrilmek yerine Java bytecode formatına derlenir.
Bu bytecode Java Virtual Machine tarafından çalıştırılır.
- Java kaynak kodu
- Java compiler
- Java bytecode
- Java Virtual Machine
- Makine kodu
Bu yaklaşım Java'nın farklı işletim sistemleri ve işlemci mimarileri üzerinde aynı bytecode ile çalışabilmesinin temel mekanizmalarından biridir.
C# ve .NET Nasıl Çalışır?
C# tarafında da benzer şekilde kaynak kod doğrudan yalnızca fiziksel işlemci komutlarına çevrilmek zorunda değildir.
C# compiler kodu .NET tarafından kullanılan Intermediate Language yapısına dönüştürür.
Uygulama çalıştırılırken .NET runtime bu ara kodu uygun makine komutlarına dönüştürebilir.
- C# kaynak kodu
- Compiler
- Intermediate Language
- .NET Runtime
- Makine kodu
JavaScript Nasıl Çalışır?
JavaScript'in çalışma modeli özellikle modern tarayıcı motorları nedeniyle oldukça gelişmiştir.
Eskiden JavaScript çoğunlukla interpreter yaklaşımıyla açıklanırken modern JavaScript motorları kodu analiz eder, ara temsiller oluşturur ve sık kullanılan kodları çalışma sırasında optimize ederek native makine koduna dönüştürebilir.
Chrome ve Node.js içerisinde kullanılan V8 gibi motorlar Just-In-Time compilation tekniklerinden yararlanır.
JIT Compiler Nedir?
JIT, Just-In-Time Compilation ifadesinin kısaltmasıdır.
JIT compiler kodun tamamını yalnızca program başlamadan önce derlemek yerine çalışma sırasında hangi kodların yoğun biçimde kullanıldığını analiz ederek bunları optimize edilmiş makine koduna çevirebilir.
Bu yaklaşım runtime sistemine uygulamanın gerçek çalışma davranışını gözlemleyebilme avantajı sağlar.
JavaScript motorları, Java Virtual Machine ve .NET runtime gibi platformlarda JIT teknolojileri önemli rol oynayabilir.
AOT Compilation Nedir?
AOT, Ahead-Of-Time Compilation anlamına gelir.
Bu yaklaşımda kod program çalıştırılmadan önce hedef platform için derlenir.
C ve C++ dünyasında klasik native compilation buna benzer bir yaklaşım kullanırken modern runtime platformları da bazı senaryolarda AOT özellikleri sunabilir.
AOT özellikle başlangıç süresinin azaltılması ve runtime sırasında daha az derleme işlemi yapılması gibi avantajlar sağlayabilir.
Linker Nedir?
Büyük bir uygulama genellikle tek bir kaynak kod dosyasından oluşmaz. Farklı dosyalar, kütüphaneler ve sistem bileşenleri birlikte kullanılır.
Compiler farklı kaynak dosyalarını object file gibi ara çıktılara dönüştürebilir. Linker ise bu parçaları bir araya getirerek çalıştırılabilir programın oluşturulmasını sağlar.
Örneğin programınız başka bir kütüphanedeki fonksiyonu kullanıyorsa linker gerekli bağlantıları çözmekten sorumlu olabilir.
Static Linking ve Dynamic Linking Nedir?
Static Linking
Programın ihtiyaç duyduğu kütüphane kodlarının çalıştırılabilir dosyanın içerisine dahil edilmesidir.
Bu yaklaşım uygulamanın daha bağımsız çalışmasını sağlayabilir ancak executable boyutunun büyümesine neden olabilir.
Dynamic Linking
Program bazı kütüphaneleri çalışma sırasında harici dosyalardan kullanabilir.
Windows sistemlerinde DLL, Linux sistemlerinde shared library yapıları bu modele örnek verilebilir.
Executable Dosya Oluştuktan Sonra Ne Olur?
Program derlendikten sonra işlem tamamlanmış değildir. Kullanıcı uygulamayı çalıştırdığında işletim sistemi devreye girer.
İşletim sistemi executable dosyayı okur ve program için yeni bir process oluşturur.
- Program belleğe yüklenir.
- Gerekli kütüphaneler bağlanır.
- Stack hazırlanır.
- Heap için bellek alanı oluşturulur.
- Programın başlangıç noktası belirlenir.
- İşlemci program komutlarını çalıştırmaya başlar.
Process Nedir?
Çalıştırılabilir dosyanın kendisi yalnızca diskte bulunan bir programdır. Program çalıştırıldığında işletim sistemi tarafından oluşturulan aktif çalışma ortamına process adı verilir.
Bir process yalnızca makine kodundan ibaret değildir.
- Bellek alanı
- Stack
- Heap
- Threadler
- Dosya tanımlayıcıları
- İşletim sistemi kaynakları
Bunların tamamı çalışan programın bir parçasıdır.
Stack Nedir?
Stack, programın çalışma sırasında kullandığı önemli bellek alanlarından biridir.
Fonksiyon çağrıları, bazı yerel değişkenler, dönüş adresleri ve geçici veriler stack üzerinde tutulabilir.
Bir fonksiyon çağrıldığında stack üzerinde yeni bir alan oluşturulabilir. Fonksiyon tamamlandığında bu alan hızlı biçimde kaldırılabilir.
Bu nedenle stack üzerinde bellek yönetimi genellikle oldukça hızlıdır.
Heap Nedir?
Heap dinamik bellek tahsisi için kullanılan başka bir bellek alanıdır.
Program çalışma sırasında boyutu önceden bilinmeyen veya fonksiyon ömründen daha uzun süre kullanılacak veriler oluşturduğunda heap kullanılabilir.
C ve C++ gibi dillerde heap belleğinin yönetimi büyük ölçüde geliştiricinin sorumluluğunda olabilirken Java, C# ve JavaScript gibi dillerde garbage collector bu işlemin önemli bölümünü otomatik olarak gerçekleştirebilir.
Garbage Collector Nedir?
Garbage Collector, uygulama tarafından artık kullanılmayan bazı bellek alanlarını tespit ederek yeniden kullanılabilir hale getiren otomatik bellek yönetim sistemidir.
Java, C#, JavaScript ve Go gibi birçok modern ortamda farklı garbage collection algoritmaları kullanılmaktadır.
Bu sistem geliştiricinin manuel bellek yönetimi yükünü azaltır ancak çalışma zamanı sırasında ek maliyet oluşturabilir.
Manuel Bellek Yönetimi Nasıl Çalışır?
C gibi dillerde geliştirici ihtiyaç duyduğu belleği kendisi ayırabilir ve işi bittiğinde bu belleği serbest bırakmakla sorumludur.
Bellek serbest bırakılmazsa memory leak oluşabilir. Serbest bırakılmış bir alana tekrar erişilirse daha ciddi bellek güvenliği problemleri ortaya çıkabilir.
Rust gibi modern sistem programlama dilleri ise garbage collector kullanmadan bellek güvenliği konusunda daha güçlü compiler kontrolleri sağlamaya çalışır.
CPU Programı Nasıl Çalıştırır?
Program makine koduna dönüştürüldükten ve belleğe yüklendikten sonra işin en alt katmanında işlemci bulunur.
CPU temel olarak bellekte bulunan komutları sürekli olarak alır, çözümler ve çalıştırır.
- Fetch: Komut bellekten alınır.
- Decode: Komutun ne yaptığı belirlenir.
- Execute: İşlem gerçekleştirilir.
Bu süreç modern işlemcilerde saniyede milyarlarca işlem ölçeğinde gerçekleşebilir.
Register Nedir?
Registerlar işlemcinin içerisinde bulunan son derece hızlı küçük veri alanlarıdır.
CPU üzerinde gerçekleştirilen matematiksel ve mantıksal işlemler sırasında veriler sıklıkla registerlarda tutulur.
Compiler'ın yaptığı önemli optimizasyonlardan biri hangi verilerin hangi registerlarda tutulacağını doğru şekilde planlamaktır.
Programlama Dili Performansı Nereden Geliyor?
Bir programlama dilinin performansı yalnızca dilin sözdiziminden kaynaklanmaz.
Gerçek performansı etkileyen çok sayıda faktör bulunur.
- Compiler kalitesi
- Runtime mimarisi
- Garbage collector davranışı
- JIT optimizasyonları
- Bellek yönetimi
- Kullanılan algoritmalar
- Cache kullanımı
- İşlemci mimarisi
- Programın nasıl yazıldığı
Bu nedenle yalnızca “C hızlıdır, Python yavaştır” gibi tek cümlelik karşılaştırmalar çoğu zaman gerçek sistem davranışını tam olarak açıklamaz.
Neden Bazı Diller Diğerlerinden Daha Hızlı?
Native olarak derlenen dillerde programın önemli bölümü daha çalıştırılmadan önce işlemci komutlarına dönüştürülebilir.
Dinamik dillerde ise veri türü kontrolü, dinamik dispatch, garbage collection veya runtime üzerinde gerçekleştirilen başka işlemler ek maliyet oluşturabilir.
Ancak modern JIT compiler sistemleri çalışma sırasında program davranışını gözlemleyerek bazı durumlarda oldukça agresif optimizasyonlar gerçekleştirebilir.
Dolayısıyla performansın nasıl oluştuğunu anlamak için yalnızca programlama diline değil compiler ve runtime mimarisine de bakmak gerekir.
Programlama Dili ile Compiler Aynı Şey mi?
Hayır.
Programlama dili bir kurallar ve davranışlar tanımıdır. Compiler ise bu dili gerçekleştiren yazılımlardan biridir.
Aynı programlama dili için farklı compiler veya runtime implementasyonları bulunabilir.
Örneğin C ve C++ dünyasında GCC ve Clang gibi farklı compiler sistemleri bulunmaktadır.
Bu nedenle bir programlama dilinin özellikleri ile onu çalıştıran implementation birbirinden ayrılmalıdır.
Programlama Dilini Kim Tanımlar?
Bir programlama dili sözdizimi, type system, çalışma davranışları, standart kütüphane ve çeşitli dil kurallarından oluşur.
Bazı diller resmi standartlarla tanımlanırken bazıları temel bir implementation ve proje dokümantasyonu üzerinden gelişir.
Compiler geliştiricileri ise bu kuralları gerçek yazılıma dönüştürür.
Bir Programlama Dili Nasıl Oluşturulur?
Yeni bir programlama dili geliştirmek isteyen bir ekip önce dilin nasıl görüneceğini ve nasıl davranacağını belirlemelidir.
- Sözdizimi tasarlanır.
- Grammar kuralları oluşturulur.
- Lexer geliştirilir.
- Parser geliştirilir.
- AST yapısı oluşturulur.
- Type system geliştirilebilir.
- Semantic analysis sistemi yazılır.
- Interpreter veya compiler backend geliştirilir.
- Runtime gerekiyorsa oluşturulur.
- Standart kütüphane geliştirilir.
Basit bir eğitim amaçlı programlama dili birkaç bin satır kodla oluşturulabilirken C++, Rust, Java veya C# seviyesindeki üretim dilleri milyonlarca satırlık compiler, runtime ve geliştirme aracı ekosistemlerine dönüşebilir.
Neden Bu Kadar Fazla Programlama Dili Var?
Bütün programlama dilleri temelde bilgisayara talimat vermek için kullanılıyorsa neden yüzlerce farklı dil bulunmaktadır?
Çünkü her programlama dili farklı problemleri ve farklı tasarım hedeflerini önceliklendirir.
- C düşük seviyeli kontrol ve taşınabilirliği önceliklendirir.
- Python geliştirici üretkenliği ve sadeliğe önem verir.
- Java platform bağımsız runtime yaklaşımını kullanır.
- C# modern uygulama ve kurumsal yazılım geliştirmeye güçlü bir ekosistem sunar.
- JavaScript web platformunun temel programlama dillerinden biridir.
- Rust performans ile bellek güvenliğini birlikte sağlamaya odaklanır.
- Go sadelik, concurrency ve backend altyapılarını önceliklendirir.
Dolayısıyla farklı programlama dilleri aslında aynı bilgisayarı farklı seviyelerde ve farklı tasarım felsefeleriyle kontrol etmenin yollarıdır.
Yapay Zeka Kod Yazdığında Bu Süreç Değişiyor mu?
Yapay zekanın kod üretmesi programlama dilinin çalışma mekanizmasını değiştirmez.
Yapay zeka yalnızca kaynak kodu oluşturan tarafı değiştirir.
Bir geliştirici veya AI Agent TypeScript kodu oluşturduğunda bu kod yine parser, type checker, compiler veya runtime gibi aynı teknik aşamalardan geçmek zorundadır.
Bu nedenle yapay zeka çağında compilerların ve güçlü doğrulama sistemlerinin önemi azalmak yerine artabilir.
Yapay zeka çok hızlı biçimde kod üretebildiği için üretilen kodun doğru olup olmadığını otomatik olarak kontrol edebilen type system, compiler, static analyzer ve test sistemleri daha değerli hale gelebilir.
Kaynak Koddan CPU'ya Kadar Tüm Zincir
Bir programlama dilinin nasıl çalıştığını tek bir zincir halinde düşündüğümüzde genel yapı şu şekilde özetlenebilir.
- İnsan veya yapay zeka kaynak kodu oluşturur.
- Lexer kodu tokenlara ayırır.
- Parser kodun yapısını oluşturur.
- AST meydana gelir.
- Semantic analysis ve type checking gerçekleştirilir.
- Ara temsil oluşturulabilir.
- Compiler optimizasyonları uygulanır.
- Machine code veya bytecode oluşturulur.
- Runtime gerekiyorsa programı yönetir.
- İşletim sistemi programı belleğe yükler.
- CPU komutları fetch, decode ve execute döngüsüyle çalıştırır.
Ekrana yazdığımız birkaç satır kodun arkasında aslında compiler teorisi, işletim sistemleri, bellek yönetimi ve işlemci mimarisinin birlikte çalıştığı oldukça büyük bir teknoloji zinciri bulunmaktadır.
Bir Yazılımcının Bunları Bilmesi Gerekir mi?
Bir web sitesi veya standart iş uygulaması geliştirmek için compiler'ın bütün iç yapısını bilmek zorunlu değildir.
Ancak programlama dilinin altında neler olduğunu anlamak geliştiricinin birçok konuda daha doğru karar vermesini sağlar.
- Performans problemlerini daha iyi anlayabilirsiniz.
- Bellek kullanımını daha doğru yorumlayabilirsiniz.
- Compiler hatalarının nedenlerini daha kolay anlayabilirsiniz.
- Farklı programlama dilleri arasındaki farkları daha doğru değerlendirebilirsiniz.
- Runtime ve native uygulamalar arasındaki farkları anlayabilirsiniz.
- Yazılım mimarisi kararlarını daha bilinçli verebilirsiniz.
Sonuç
Bir programlama dili aslında bilgisayarın doğrudan anladığı bir iletişim biçimi değildir. İnsanların karmaşık bilgisayar sistemlerini daha kolay kontrol edebilmesi için oluşturulmuş bir soyutlama katmanıdır.
Yazdığımız kaynak kod lexer, parser, AST, semantic analysis, compiler, interpreter, runtime ve işletim sistemi gibi farklı katmanlardan geçerek sonunda işlemcinin anlayabileceği komutlara dönüşür.
Python, JavaScript, C#, Java, C++, Rust veya başka bir dil kullanıyor olmamız bu temel gerçeği değiştirmez. Yol farklı olabilir ancak sonunda bütün programlar fiziksel işlemcinin gerçekleştirebildiği düşük seviyeli işlemlere ulaşır.
Programlama dillerinin nasıl çalıştığını anlamak yalnızca compiler geliştirmek isteyenler için önemli değildir. Bu bilgi aynı zamanda bir yazılımcının kullandığı araçların arkasındaki sistemi anlamasını ve farklı teknolojiler arasında daha bilinçli seçimler yapmasını sağlar.
Kodun ekranda gördüğümüz kısmı yalnızca başlangıçtır. Asıl süreç, o kodun bilgisayarın anlayabileceği hale dönüştürüldüğü görünmeyen katmanlarda gerçekleşir.
Etiketler
İlgili yazılar