215 lines
12 KiB
Markdown
215 lines
12 KiB
Markdown
# saQut Frontend Tasarım Oturumu — Transkript
|
||
|
||
> Bu belge, frontend (symbol table + semantic analiz + optimizasyon) mimarisinin
|
||
> kararlaştırıldığı tasarım oturumunun **sadık bir transkriptidir.** Alınan
|
||
> kararların gerekçeleri ADR formatında `docs/adr-frontend-analiz.md`'de,
|
||
> uygulama planı `docs/roadmap-frontend.md`'de yer alır. Bu belge ise tartışmanın
|
||
> **akışını ve düşünce sürecini** korur — bir kararın nasıl olgunlaştığı, hangi
|
||
> itirazların nasıl çözüldüğü buradadır.
|
||
|
||
---
|
||
|
||
## 0. Başlangıç durumu
|
||
|
||
Oturum öncesi tamamlananlar:
|
||
- Aşama 0 (SourceLocation / konum takibi) bitirildi: her token ve AST node'u
|
||
dosya/satır/sütun/offset taşıyor.
|
||
- İki bug giderildi: (1) `location.file` boş geliyordu → `Tokenizer::scan` artık
|
||
dosya yolu alıyor; (2) `"kind": true` bug'ı → `JsonObject::add`'a `const char*`
|
||
overload'u eklendi.
|
||
- AST JSON çıktısındaki fazla boşluk/girinti hataları temizlendi.
|
||
- Bu işler commit'lendi, master'a merge edildi ve push edildi.
|
||
|
||
---
|
||
|
||
## 1. "Rotamız ne? IR/JIT'e geçebilir miyiz?"
|
||
|
||
**Kullanıcı:** AST ve parser hazır mı? IR ve JIT adımına geçebilir miyiz?
|
||
|
||
**Asistan (özet):** Henüz değil, doğrudan atlamak riskli. Roadmap durumu:
|
||
- Aşama 0 (konum) ✅
|
||
- Aşama 1 (CLI/REPL) 🟡 yarım
|
||
- Aşama 2 (AST bellek canavarı) 🟡 yarım
|
||
- Aşama 3 (Symbol Table) ❌ yok (`src/symbol/` klasörü bile yok)
|
||
- Aşama 4 (Feature Toggle) ❌ yok
|
||
- Aşama 5 (IR + Backend) ❌ embriyonik (IR'de sadece 5 opcode: mathadd/sub/mul/div,
|
||
declare; backend yok)
|
||
|
||
JIT'e atlamamak için iki engel: (1) Symbol table yok — IR üretimi değişken
|
||
çözümlemesi ister; (2) IR'de kontrol akışı/fonksiyon/bellek opcode'ları yok, yani
|
||
if/while/fonksiyon derlenemez. Ayrıca ADR-001'e göre strateji: C transpile →
|
||
QBE → custom/JIT. JIT "uzun vade".
|
||
|
||
Önerilen sıra: Symbol Table → IR güçlendirme → C transpile → sonra JIT/QBE.
|
||
|
||
---
|
||
|
||
## 2. Kullanıcının vizyonu: 2-pass parser ve AST'yi derinleştirme
|
||
|
||
**Kullanıcı (özet):** Parser 2 aşamalı olacak. İlk pass syntax'ı çözüp basit
|
||
AST'yi bırakacak (tokenler, yerleri, türleri). İkinci pass'te symbol table
|
||
kurulacak (değişkenler, türleri, yerleri, kapsamları, kaç kez/nerede kullanıldıkları).
|
||
Sonraki tüm operasyonlar AST üzerinden döneceği için AST'de çok fazla ham/işlenmiş
|
||
veri olmalı; optimizasyon ve analizler tamamen burada olacak. 2. pass'te ilk AST
|
||
ve symbol table kullanılarak: kullanılmayan değişkenler, return sonrası kod,
|
||
bir kez atanıp bir kez kullanılan değişkenler, literal-operatör-literal (`1+2`)
|
||
kapatılacak (constant folding). Yorumlar/eleştiriler isteniyor.
|
||
|
||
**Asistan — 4 itiraz/uyarı:**
|
||
|
||
1. **"AST'yi derinleştirmek" felsefeyle çelişebilir.** "Hiçbir bilgi atılmaz"
|
||
prensibi var. `1+2`'yi yıkıcı şekilde `3` yaparsan orijinali silersin →
|
||
`saqut ast` artık kullanıcının yazdığını göstermez. Çözüm: **annotation**
|
||
(node'a `foldedValue:3` ekle, ağacı bozma) vs **transformation** (asıl silmeyi
|
||
AST→IR indirmede yap).
|
||
|
||
2. **"2. pass" tek pass değil, içine 3 iş tıkıyorsun:** (a) sembol toplama/isim
|
||
çözümleme, (b) semantik doğrulama, (c) optimizasyon. Bunlar farklı bağımlılık
|
||
sırasına sahip; optimizasyon semantik analiz bitince anlamlı; optimizasyonlar
|
||
birbirini tetikler (fixpoint gerekir); ayrı test edilmeli.
|
||
|
||
3. **"Bir kez atanıp bir kez kullanılan" optimizasyonu ağır.** Def-use chain /
|
||
dataflow ister; basit tree-walk değil. Ertele, IR'de yap.
|
||
|
||
4. **Tip kontrolünü atlamamış ol.** Symbol table tip tutacaksa, `int x = "..."`
|
||
hatasını kim yakalar? Her expression node'una sonuç tipi annotate etmek değerli.
|
||
|
||
İki soru soruldu: constant folding yıkıcı mı annotation mı? Scope kuralı
|
||
(forward reference) ne?
|
||
|
||
---
|
||
|
||
## 3. Kullanıcının netleştirmesi: pass = derleyici aşaması; optimizasyon nerede?
|
||
|
||
**Kullanıcı (özet):** "2-pass" sadece parser içindi, optimizasyonu kastetmedim.
|
||
Sıralama: 1. pass AST kur, 2. pass symbol table, 3. pass AST'yi derinleştir.
|
||
Ölü kodu 3'te sadece işaretleyelim demiştim ama haklısın, optimizasyon kendi
|
||
içinde tekrar çalışmalı — belki 5, belki 10 pass; her pass öncekinden rahat.
|
||
|
||
**Önemli soru:** Optimizasyonları tam derleme/IR üretme zamanından önce
|
||
yapmamız gerekmiyor mu? Neden AST aşamasında optimize edeceğimizi düşündün?
|
||
Parametre olarak AST isteniyorsa optimizasyon halini isteğe bağlı vermeliyiz;
|
||
kullanıcı AST'nin/symbol table'ın optimizasyon öncesi/sonrası halini görmek
|
||
isteyebilir.
|
||
|
||
**Scope:** Bunu JIT veya compiler frontend olarak düşünüyorum; arka planda Java
|
||
gibi forward reference olan ama C syntax'lı bir dil. Başta OOP yok, JS gibi düşün.
|
||
|
||
**Asistan:**
|
||
- "Pass" düzeltmesi kabul: "5-10 pass" değil **fixpoint** (değişiklik durana
|
||
kadar). Her **tur** daha az iş yapar; analiz pass'leri bir kez çalışır.
|
||
- "Neden AST'de optimize?" sorusuna dürüst cevap: Ben "optimize" değil
|
||
"annotate" demiştim. **Analiz (gerçekler) vs optimizasyon (değişiklik)** ayrımı.
|
||
Frontend/middle-end/backend katmanlaması tanıtıldı. Basit kaynak-seviyesi
|
||
optimizasyonlar AST'de (backend-bağımsız, incelenebilir); ağır dataflow
|
||
optimizasyonları IR'de. Backend'e koyarsak 3 backend'de 3 kez yazarız.
|
||
- **Kullanıcının "öncesi/sonrası görebilmeli" gereksinimi mimariyi belirledi:**
|
||
optimizasyon orijinali ASLA bozmamalı → klon üret. `saqut ast` öncesi,
|
||
`saqut ast --optimized` sonrası.
|
||
- Scope kararı önerildi: üst seviye tam forward reference (2 alt-geçiş), lokal
|
||
declare-before-use. "Local hoisting istiyor musun?" sorusu soruldu.
|
||
|
||
---
|
||
|
||
## 4. Tipli dil, hoisting yok; kalan tasarım sorunları
|
||
|
||
**Kullanıcı (özet):** JS'i sadece syntax basitliği için örnek verdim, kötü
|
||
yanlarını almıyoruz — null/undefined karmaşası yok. Lokal'de hoisting olmasın,
|
||
tipli dil olacak. Pass = derleyicinin kendi aşamaları (lexing/parsing/optimizing/
|
||
compiling), bunları çoğaltabiliriz. Scope: tam forward reference olsun.
|
||
Başka sorun var mı?
|
||
|
||
**Asistan — "pass = aşama" doğrulandı,** her aşama bağımsız çalışan modül; CLI
|
||
komutları bunun yansıması. Sonra **3 gerçek mesele + küçük kararlar:**
|
||
|
||
1. **Tip sistemi hiç tanımlanmadı.** `varType` şu an sadece string. Minimal ama
|
||
genişletilebilir `Type` sınıfı gerek. Tip çıkarımı (auto)? Implicit conversion?
|
||
2. **Diagnostic altyapısı şimdi gelmeli.** Semantik analiz hata üretecek ama
|
||
koyacak yer yok. Aşama 6'nın tamamı değil ama minimal `Diagnostic` toplayıcı
|
||
bu işle birlikte gelmeli.
|
||
3. **Annotation'lar node'larda nerede yaşayacak?** `ExpressionNode`/`StatementNode`
|
||
ara tabanları önerisi.
|
||
|
||
Küçük kararlar: overloading yok; scope granülerliği (Program/FunctionDecl/Block/
|
||
for-while); recursive struct → pointer gelince; aritmetik semantiği (5/2 int mi).
|
||
|
||
---
|
||
|
||
## 5. Kullanıcının kapsam kararları + sorular
|
||
|
||
**Kullanıcı (özet):** En baştan: pointer sistemi yok, tipleme basit, tuple
|
||
(`<T,U>`) yok, class yok struct var, array'ler define sırasında `[]` ile —
|
||
ama dinamik mi statik bellek mi karar veremedim. Fonksiyonlar tipli. `class`
|
||
gibi keyword'leri dikkate alma, `auto` yok. Otomatik float↔int dönüşümü yok,
|
||
her şey açık; gizli dönüşüm istemiyorum, sadece constant'larda (`5/2`→`2`).
|
||
Minimal Type sınıfına katılıyorum ama ileride geliştirilebilir olmalı — bu dilin
|
||
geleceğini bilmiyorum, popüler de olabilir, tozlanabilir de. Diagnostic için tüm
|
||
hataları baştan belirlemeliyiz; sistemi durdurup tüm hataları kullanıcıya
|
||
söylemeliyiz; hata raporlama fonksiyonları ayrı bir yerde olsun. Recursive struct
|
||
define yok ama `struct StructA { StructB BVar }` olabilir. Scope granülerliği
|
||
onaylandı. Duplicate'i tamamen yok et. For döngüsü içindeki objeler dışında
|
||
silinebilir — ama symbol table'ı karmaşıklaştırır, her katmanda namespace
|
||
tutmalıyız; üst katmanda hem öncesi hem sonrası aranacak — symbol table da mı 2
|
||
geçiş olacak? AST'nin temiz kalması neyi zorlaştırır bilmiyorum, sen karar ver.
|
||
AST node cpp dosyaları neredeyse boş, oraları kullanalım, o class'ları dolduralım.
|
||
|
||
**ExpressionNode konusunu tam anlamadım, açar mısın?** Ben tüm analizi AST'de
|
||
yapar, her node'da anlamlı veri tutar, ref'leri kendi içimde pointer olarak
|
||
tutarım diye düşünüyordum.
|
||
|
||
**Asistan cevapları:**
|
||
- **Array:** Pointer yoksa dinamik array kuramazsın (heap+gizli pointer ister).
|
||
Öneri: statik sabit boyut. *(Bu karar bir sonraki turda revize edildi — bkz. §6.)*
|
||
- **ExpressionNode = aslında aynı fikirdeyiz.** Sorum felsefi değil, C++ detayı:
|
||
`resolvedType`'ı hangi node'a koyacağız? Tipli dilde sadece ifadelerin tipi var
|
||
(`5+3`→int; `if`→tipsiz). İki ara taban (`ExpressionNode`/`StatementNode`)
|
||
alanları doğru yere koyar + "burası ifade olmalı" kontrolü sağlar. Analiz yine
|
||
AST'de yaşar; bu sadece alanları doğru sınıflara dağıtmak.
|
||
- **Symbol table 2 geçiş?** Evet ama sadece üst seviyede. Geçiş 1: global'leri
|
||
hoist et. Geçiş 2: gövdelere in, çöz. Fonksiyon içi tek geçiş (lokal'de forward
|
||
ref yok). Öncesi/sonrası derdi sadece global'ler için, onu Geçiş 1 çözer.
|
||
- **AST temiz vs her şey AST'de → karar: her şey AST'de** (senin modelin).
|
||
Optimizasyon klonda dönüştürür. **Önemli:** "kaç kez kullanıldı" Symbol'da
|
||
yaşar (değişkene ait), node'da değil; IdentifierNode Symbol'a pointer tutar.
|
||
- **Hoisting açıklaması** (kullanıcı "local hoisting ne demek, global=local değil
|
||
mi?" diye sordu): Hoisting = tanımın yazıldığı satırdan önce de görünmesi.
|
||
Global'de forward ref güvenli (gövde tanımda çalışmaz, çağrılınca çalışır).
|
||
Lokal'de declare-before-use (değişkenin sırası/değeri var). Asimetri =
|
||
tutarsızlık değil, tam olarak Java'nın davranışı.
|
||
|
||
---
|
||
|
||
## 6. Pointer netleştirmesi + son onaylar
|
||
|
||
**Kullanıcı (özet):** Yanlış anlama düzeltmesi: pointer derleyici **içinde** tabii
|
||
ki var; programlama **dilinde** `*`/`&` yok. Pointer sadece derleyicinin içinde.
|
||
Bu, değişken listelerini destekler — JS'te pointer syntax'ı yok ama array var.
|
||
Kullanıcıya pointer yok, arka planda A'dan Z'ye pointer var. Ayrıca: local
|
||
hoisting senin dediğin gibi (declare-before-use). Array'ler `int[]` olsun.
|
||
**Ama bu backend'de dinamik bellek zorunluluğu getirmez mi? JIT'te?**
|
||
ExpressionNode → evet ekle. Her şey temiz, anlaşılır, yorum satırlarıyla
|
||
takip edilebilir olsun yeter.
|
||
|
||
**Asistan:**
|
||
- **Array kararı revize edildi:** Pointer arka planda olduğuna göre dinamik
|
||
array mümkün. Frontend için `Type` sadece "array of T" temsil etsin, boyut
|
||
tipte olmasın. Runtime bellek modeli (statik/heap/free) backend kararı, ertelendi.
|
||
- **Dinamik bellek/JIT yükümlülüğü cevabı:** Evet ileride yükümlülük getirir ama
|
||
frontend'i bloklamaz. C transpile'da malloc/realloc/free ile neredeyse bedava;
|
||
JIT'te minik runtime kütüphanesine call emit edilir; yönetim stratejisi en
|
||
basiti scope-tabanlı ownership, GC gerekmez.
|
||
- Tüm kararlar kilitlendi; dosya-dosya yol haritası sunuldu (Faz 0…4).
|
||
|
||
---
|
||
|
||
## 7. Belgeleme talebi
|
||
|
||
**Kullanıcı:** Konuştuklarımızı ve kararları ADR formatında, neden öyle yaptığımızı
|
||
da belgele; hiçbir şey boş geçmesin; transkript de yazabilirsin; en son yol
|
||
haritasını oluştur; her şeyden önce yeni bir branch çık.
|
||
|
||
**Asistan:** `feature/frontend-analysis` branch'i açıldı. Üç belge üretildi:
|
||
- `docs/adr-frontend-analiz.md` (ADR-006…014)
|
||
- `docs/transkript-frontend-tasarim.md` (bu belge)
|
||
- `docs/roadmap-frontend.md` (uygulama planı)
|