docs: frontend tasarim kararlari, transkript ve yol haritasi

Symbol table + semantic analiz + optimizasyon framework tasarim
oturumunun ciktilari:
- adr-frontend-analiz.md: ADR-006..014 (cok-asamali frontend, analiz/
  optimizasyon ayrimi, optimizasyon konumu, fixpoint, tip sistemi,
  scope/forward-reference, ExpressionNode/StatementNode, analiz verisi
  yeri, dil kapsami) — her karar gerekcesiyle.
- transkript-frontend-tasarim.md: oturum akiscinin sadik transkripti.
- roadmap-frontend.md: dosya-dosya uygulama plani (Faz 0..4).
This commit is contained in:
abdussamedulutas 2026-06-14 17:56:42 +03:00
parent b986f0cb26
commit f7467f18b3
3 changed files with 782 additions and 0 deletions

389
docs/adr-frontend-analiz.md Normal file
View File

@ -0,0 +1,389 @@
# saQut Derleyici — Frontend & Semantic Analiz Karar Kaydı (ADR-006 …)
> Bu belge, `docs/fikirler.md`'deki ADR-001…005'in **devamıdır**. Orada backend
> stratejisi, parser mimarisi, header-only tercihi, token sistemi ve IR tasarımı
> kararlaştırılmıştı. Bu belge ise **frontend'in tamamlanması** — symbol table,
> semantic analiz ve optimizasyon framework'ü — etrafında alınan kararları,
> **neden** alındıklarını, elenen alternatifleri ve gelecekteki sonuçlarını kaydeder.
>
> Bu kararlar bir tasarım oturumunda (kullanıcı + asistan) tartışılarak alındı.
> Tartışmanın tam akışı için bkz. `docs/transkript-frontend-tasarim.md`.
> Uygulama planı için bkz. `docs/roadmap-frontend.md`.
---
## ADR-006: Çok-Aşamalı (Multi-Pass) Frontend Mimarisi
### Bağlam
Derleyici tek bir monolitik geçişle çalışmaz; lexing, parsing, analiz,
optimizasyon, kod üretimi gibi **birbirinden bağımsız aşamalardan** oluşur.
saQut'un "alet çantası" (toolbox) felsefesi gereği bu aşamaların her biri:
- bağımsız çalışabilmeli,
- net bir girdi/çıktı sözleşmesine sahip olmalı,
- gerektiğinde çoğaltılabilmeli (projenin amacına sadık kalarak).
CLI komutları (`tokens`, `ast`, `symbols`, …) zaten bu aşama-modülü
yapısının dışa vurumudur — her komut bir aşamanın çıktısını gösterir.
### Değerlendirilen Yaklaşımlar
#### Tek geçişli (single-pass) parser+analiz
- **+** Basit, hızlı.
- **** Forward reference (ileri başvuru) imkânsızlaşır; her şey tanımdan
önce bilinmek zorunda kalır.
- **** Analiz ve syntax iç içe girer, test edilemez, bakımı zor.
#### Çok geçişli (multi-pass) — net aşamalar
- **+** Her aşama tek bir iş yapar, ayrı ayrı test edilir.
- **+** Forward reference mümkün olur.
- **+** Aşamalar incelenebilir (`saqut ast`, `saqut symbols` …).
- **** Daha fazla kod ve veri yapısı; aşamalar arası sözleşme tasarımı gerekir.
### Karar
**Çok-aşamalı frontend.** Aşamalar şu üç katmana ayrılır (klasik derleyici
mimarisi):
```
FRONTEND MIDDLE-END BACKEND
lexer → token → optimizasyon IR lowering →
parser → AST → (opsiyonel, iteratif, kod üretimi
symbol table → toggle'lı, ortak (C transpile /
semantic analiz gösterim üstünde) QBE / JIT)
(annotated AST)
```
- **Pass 1 (Syntax):** token → ham AST. (Büyük ölçüde mevcut.)
- **Pass 2 (Symbol):** AST → SymbolTable (scope'lu, iki-geçişli — bkz. ADR-011).
- **Pass 3 (Semantic / "ASTyi derinleştir"):** symbol table + AST kullanılarak
her node zenginleştirilir (tip, symbol bağı, erişilebilirlik, reference sayısı).
"Parser ve symbol hikayesini bitirmek" = **frontend'i bitirmek.** Optimizasyon
ayrı bir katmandır (middle-end), backend'ler bu ortak çıktıdan beslenir.
**Neden bu katmanlama?** Birden çok backend (C transpile, QBE, JIT) planlandığı
için, ortak işler (analiz, optimizasyon) **bir kez** ortak katmanda yapılmalı;
yoksa her backend aynı optimizasyonu yeniden yazar.
---
## ADR-007: Analiz (Annotation) ile Optimizasyon (Transformation) Ayrımı
### Bağlam
`docs/fikirler.md` ve `docs/todo.md`'nin temel prensibi: **"AST bellek canavarı.
Hiçbir bilgi atılmaz."** Aynı zamanda constant folding (`1+2` → `3`), dead code
elimination gibi optimizasyonlar isteniyor. Bu ikisi doğrudan çelişir gibi
görünür: optimizasyon AST'yi bozarsa, kaynak kodun izdüşümü kaybolur ve
`saqut ast` artık kullanıcının yazdığını değil, optimize edilmiş hali gösterir.
Ayrıca kritik bir kullanıcı gereksinimi belirlendi: **kullanıcı, AST'nin veya
sembol tablosunun optimizasyondan önceki ve sonraki halini ayrı ayrı görebilmeli.**
### İki Kavram
- **Analiz (annotation) = programın gerçekleri.** "Bu node sabit, değeri 3",
"bu kod erişilemez", "bu ifadenin tipi int", "bu değişken 2 kez kullanıldı".
Bunlar **değişiklik değil, tespittir.** Backend'den bağımsızdır.
- **Optimizasyon (transformation) = ağacı/IR'ı gerçekten değiştirmek.**
`1+2`'yi `3` ile değiştirmek, ölü kodu silmek.
### Karar
✅ **İki kavram net ayrılır:**
1. **Analiz, orijinal AST'nin üstüne yerinde işaretleme yapar** (node'lara tip,
symbol bağı, erişilebilirlik, constness ekler). Ağacı **bozmaz**, zenginleştirir.
Orijinal AST hâlâ kaynak kodun tam izdüşümüdür.
2. **Optimizasyon dönüşümü, ağacın bir KOPYASI (klon) üzerinde yapılır.**
Orijinal analizli AST = "öncesi"; klon + dönüştürülmüş AST = "sonrası".
Ağaç klonlamak ucuz ve basittir, yalnızca `--optimized` istendiğinde yapılır.
**Sonuç:** Hem "bellek canavarı" felsefesi korunur (orijinal AST her şeyi tutar),
hem optimizasyon yapılır, hem de öncesi/sonrası ayrı ayrı incelenebilir.
```
saqut ast file.sqt → ham + annotate edilmiş AST (1+2 burada durur)
saqut ast file.sqt --optimized → klon, folding uygulanmış (3 var)
```
---
## ADR-008: Optimizasyon Konumu — AST mı, IR mı?
### Bağlam
"Optimizasyonu IR/derleme zamanında mı yapmalıyız, yoksa AST aşamasında mı?"
sorusu tartışıldı. İki uç yaklaşım var:
- **AST seviyesi:** kaynak-seviyesi, dile yakın, incelenebilir.
- **IR seviyesi:**ık kontrol-akış grafiği (CFG), dataflow analizi için uygun
(LLVM modeli).
### Karar
✅ **Hibrit, optimizasyon türüne göre bölünür:**
- **Kaynak-seviyesi, ağaç-yerel optimizasyonlar** (constant folding, ölü kod
işaretleme, unused variable) → **AST'de** yapılır. Çünkü:
1. Dil JS gibi basit; ağır optimizasyona ihtiyaç yok.
2. Backend-bağımsız → C transpile, QBE, JIT üçü birden faydalanır.
3. İncelenebilir kalır (`saqut ast --optimized`) — projenin varlık sebebi.
- **CFG/dataflow gerektiren optimizasyonlar** ("bir kez atanıp bir kez
kullanılan değişken" = copy propagation, common subexpression elimination,
loop optimizasyonları) → **IR'de** yapılır, IR olgunlaşınca ertelenir.
Çünkü bunlar açık kontrol akışı ister, ağaçta yapmak işkencedir.
**Neden backend'e bırakmıyoruz?** 3 backend varsa, optimizasyon backend'e
konulursa 3 kez yazılır. Ortak katmanda (middle-end) bir kez yazılır.
---
## ADR-009: Optimizasyon Pass Yönetimi — Sabit Sayı Değil, Fixpoint
### Bağlam
Optimizasyon adımları birbirini tetikler: constant folding yeni ölü kod doğurur,
dead code elimination yeni kullanılmayan değişken doğurur. Tek geçişte zincirleme
fırsatlar kaçırılır. "5 pass mı, 10 pass mı çalıştıralım?" sorusu yanlış kurgu.
> **Not:** Buradaki "pass", ADR-006'daki derleyici aşamalarından (lexing/parsing
> gibi makro-aşamalar) farklıdır. Burada "pass" = tek bir optimizasyon adımının
> AST üzerindeki bir gezisidir.
### Karar
**Fixpoint döngüsü.** Önceden belirlenmiş sayıda değil; bir pass havuzu,
**hiçbir pass değişiklik yapmayana kadar** döngüde çalışır. Belki 2 tur sürer,
belki 7 — kodun kendisi belirler.
- Her **tur** bir öncekinden daha az iş yapar (giderek azalan değişiklik), ta ki
sıfır değişiklikle stabilize olana kadar.
- Pass'ler `CompilerConfig` ile tek tek açılıp kapatılabilir.
- `OptimizationManager` pass listesini tutar, sırayı ve fixpoint döngüsünü yönetir.
> Düzeltme notu: "Her pass bir öncekinden daha kolay" sezgisi yanlıştır. Doğrusu:
> her **tur** daha az değişiklik yapar. Analiz pass'leri (symbol table, type check)
> "kolaylaşmaz"; onlar bir kez çalışır.
---
## ADR-010: Tip Sistemi Tasarımı
### Bağlam
Dil **tipli** olacak. Şu anda `varType`/`returnType` AST'de yalnızca
`std::string`. Tip kontrolünü string karşılaştırmasıyla yazmak kırılgandır ve
`int[]`, `struct Point`, fonksiyon tipi gelince baştan yazmayı gerektirir.
### Alınan Kararlar
**Minimal ama genişletilebilir `Type` sınıfı** (`src/core/type.hpp`):
- `kind`: `Primitive / Array / Struct / Function / Error`.
- Primitifler: `int, float, double, char, string, bool, void`.
- `Array` → eleman tipi (boyut tipin parçası DEĞİL — bkz. aşağı).
- `Function` → dönüş tipi + parametre tipleri.
- İleride `Pointer`, `Generic` eklenebilir.
**`Error` tipi şart.** Tip hatası olduğunda node'a `Error` atanır; böylece
ardışık sahte hatalar üretilmez (tek hata, tek mesaj).
**Gizli (implicit) dönüşüm YOK.** `int → float` otomatik olmaz; her şey açık.
- **Tek istisna:** sabit ifadelerde (constant folding) — `int a = 5 / 2;``2`.
Sabitler üzerinde küçük analiz/hesap yapılır.
**Tip çıkarımı (auto/var) YOK.** Her şey açıkça tiplenir. `auto` keyword'ü
yok sayılır. Sebep: basitlik, öngörülebilirlik, kafa karışıklığını önlemek.
**Array tip temsili: `int[]` (boyut tipte yok).** `int[]` sadece "int dizisi";
boyut tip eşitliğine girmez (JS gibi). Tip kontrolü basit kalır.
**Neden genişletilebilir?** "Bu dilin geleceğini bilmiyoruz; beklenenden popüler
de olabilir, yıllarca repolarda tozlanabilir de." Temel sağlam ve büyümeye açık
olmalı.
---
## ADR-011: Scope ve Forward Reference Kuralları
### Bağlam
Dil "Java gibi forward reference, C gibi syntax, başta OOP yok" olarak
tasarlandı (JS yalnızca **syntax basitliği** örneği olarak verildi; JS'in kötü
yanları — null/undefined ikiliği, var hoisting — **alınmıyor**).
### "Hoisting" nedir?
Bir tanımın, yazıldığı satırdan **önce de** görünür olması (scope'un tepesine
"kaldırılmış" gibi).
### Karar
✅ **Asimetrik kurallar (tam olarak Java'nın davranışı):**
- **Üst seviye (global): tam forward reference (hoisting var).** Fonksiyonlar,
global değişkenler, struct'lar sırasından bağımsız her yerde görünür.
```
int main() { return kare(5); } // kare aşağıda ama görünür → OK
int kare(int n) { return n * n; }
```
**Neden güvenli?** `main`'in gövdesi tanımlandığı anda çalışmaz; çağrılınca
çalışır, o ana kadar `kare` zaten vardır. Tanımların çalışma sırası yoktur.
- **Lokal (fonksiyon içi): declare-before-use (hoisting YOK).**
```
int main() {
int x = y + 1; // HATA: y henüz tanımlı değil
int y = 5;
}
```
**Neden?** Lokal değişkenin bir çalışma sırası ve değeri vardır; tanımdan önce
kullanmak, var olmayan/değeri olmayan bir şeyi kullanmaktır. Local hoisting
olsaydı isim olur ama değeri çöp/undefined olurdu (JS `var` derdi) — kaçınılan
durum.
**Asimetri tutarsızlık değildir:** global tanımlar yerinde çalışmaz (forward ref
güvenli), lokal değişkenlerin sırası ve değeri vardır (declare-before-use güvenli).
Bu, Java/C#'ın da davranışıdır.
**Duplicate kesinlikle yasak.** Aynı scope'ta aynı isimli iki
değişken/fonksiyon tanımlanamaz → diagnostic. (Overloading yok.)
**Shadowing serbest.** İç scope, dış scope'u gölgeleyebilir (hata değil).
**Scope oluşturan node'lar:** `Program` (global), `FunctionDecl` (parametreler),
`Block`, `for`/`while` (init değişkeni döngüye ait; döngü dışında görünmez).
Her katman bir namespace tutar; değişken bulunamazsa bir üst katmanda aranır.
### Symbol Table'ın İki Geçişi
✅ **Sadece üst seviyede iki geçiş gerekir:**
- **Geçiş 1:** tüm üst-seviye tanımları (fonksiyon imzaları, struct isim+alanları,
global değişkenler) global scope'a hoist et.
- **Geçiş 2:** gövdelere in; lokal'leri declare-before-use ile topla, her
`Identifier`'ı çöz, reference ekle.
- **Fonksiyon içi tek geçiş yeter** (lokal'de forward ref yok). "Öncesi/sonrası"
derdi yalnızca global'ler içindir, onu da Geçiş 1 çözer (global'ler en baştan
tamamen doludur).
---
## ADR-012: ExpressionNode / StatementNode Ara Tabanları
### Bağlam
Şu anda tüm AST node'ları doğrudan `ASTNode`'dan türüyor; "ifade" (değer üreten)
ve "deyim" (iş yapan ama değer olmayan) ayrımı yok. Tipli bir dilde yalnızca
**ifadelerin** tipi vardır: `5 + 3` → int; `if (...) {...}` → tipi yok.
`resolvedType` alanını nereye koyacağımız bir tasarım kararı. Seçenekler:
- (a) `ASTNode` tabanına koy → her node'da olur, `if`/`while`'da boşa durur.
- (b) `ExpressionNode`/`StatementNode` ara tabanları → alanlar doğru yere oturur.
- (c) Yan-tablo `map<ASTNode*, Annotation>` → AST temiz ama dolaylı/karmaşık.
### Karar
**(b) İki ara taban eklenir:**
- `ExpressionNode : ASTNode``resolvedType`, `isConstant`, `foldedValue`.
- `StatementNode : ASTNode``isReachable` (ölü kod analizi için).
```
ASTNode
├─ ExpressionNode (resolvedType, isConstant, foldedValue)
│ ├─ LiteralNode / BinaryExpressionNode / IdentifierNode / CallExpressionNode …
└─ StatementNode (isReachable)
├─ IfStatementNode / WhileStatementNode / ReturnStatementNode / BlockNode …
```
**Kazanımlar:**
1. `resolvedType` yalnızca tip taşıyabilen node'larda olur.
2. Parser/analiz "burası ifade olmalı" diyebilir (örn. `if` koşulu bir
`ExpressionNode` olmalı, fonksiyon argümanı `ExpressionNode` olmalı).
**Önemli:** Bu karar, "her şey AST'de" felsefesini bozmaz (bkz. ADR-013); yalnızca
analiz alanlarını doğru node sınıflarına dağıtır. Node cpp dosyaları zaten boştu;
bu tabanlar onları doldururken ekleniyor. Maliyeti şimdi düşük, sonra yüksek
olurdu.
---
## ADR-013: Analiz Verisi Nerede Yaşar — Her Şey AST'de
### Bağlam
İki model: (1) her şey AST node'larının üstünde; (2) AST temiz, analiz sonuçları
ayrı yan-tablolarda.
- **Her şey AST'de:** tek doğruluk kaynağı, gezinmesi kolay (`node->type`),
kullanıcının zihinsel modeli, boş node class'larını doldurur. Ancak öncesi/
sonrası için ağacı klonlamak gerekir.
- **Temiz AST + yan-tablolar:** AST sade kalır, çoklu bağımsız analiz mümkün;
ancak dolaylılık ve karmaşıklık artar, "node class'larını doldur" isteğine ters.
### Karar
**Her şey AST node'larının üstünde** (kullanıcının modeli):
- **Analiz (tip, constness, erişilebilirlik) = node'lara yerinde işaretlenir.**
- **Optimizasyon dönüşümü = ağacın klonunda yapılır** (ADR-007), böylece
öncesi/sonrası korunur.
✅ **Önemli ayrım — "kaç kez kullanıldı" bilgisi node'da değil, Symbol'da:**
- `IdentifierNode` → işaret ettiği `Symbol`'a pointer tutar.
- `Symbol` → o değişkenin tüm referanslarının listesini + sayısını tutar.
- `ExpressionNode` → kendi sonuç tipini, sabit olup olmadığını tutar.
Sebep: kullanım sayısı **değişkene** aittir, tek bir kullanım node'una değil.
> **Pointer notu:** Burada ve genel olarak derleyici **içinde** pointer serbestçe
> kullanılır (Symbol bağları, parent pointer'lar vb.). Kullanıcıya sunulan
> **dilde** pointer syntax'ı (`*`, `&`) yoktur — bkz. ADR-014.
---
## ADR-014: Dil Kapsamı ve Özellik Kararları
### Karar — Başlangıç Dili (v0)
| Özellik | Karar | Not |
|---|---|---|
| Pointer (kullanıcı syntax'ı `*`/`&`) | ❌ Yok | Ama derleyici/runtime **içeride** pointer'ı sonuna kadar kullanır |
| Tuple / Generic (`<T,U>`) | ❌ Yok | |
| Class / OOP | ❌ Yok (başta) | `class` keyword'ü yok sayılır |
| Struct | ✅ Var | `struct A { B bVar }` olur (B başka yerde tanımlı); recursive define yok |
| Array | ✅ `int[]` | Dinamik yönde; runtime bellek modeli ertelendi |
| Fonksiyonlar | ✅ Tipli | Dönüş + parametre tipleri zorunlu |
| `auto` / tip çıkarımı | ❌ Yok | Her şey açık tipli |
| Gizli int↔float dönüşümü | ❌ Yok | Sadece sabit folding'de istisna |
### Dinamik Array'in Bellek Yükümlülüğü (Gelecek Notu)
`int[]` büyüyebilen array = heap + bir yönetim stratejisi gerektirir. Bu gerçek
bir yükümlülüktür ama **frontend'i bloklamaz** ve kolay yolu vardır:
- **Frontend:** yalnızca "bu int dizisi" bilgisini ister; bellek modelinden habersiz.
- **C transpile backend (ilk backend):** `int[]` → C'de `struct {int* data; size_t len, cap;}`,
`malloc/realloc/free` ile yönetilir. Bellek yönetimi C'den hazır gelir.
- **JIT backend:** bellek yönetimini kendi yapmaz; minik bir runtime kütüphanesi
(`array_new`, `array_push`, `array_free`) olur, JIT bunlara **call** emit eder.
- **Yönetim stratejisi (ne zaman free):** en basiti scope-tabanlı ownership
(array'i tutan değişken scope'tan çıkınca free). GC gerekmez. Runtime'a
gelince kararlaştırılır.
---
## Kararların Özet Tablosu
| ADR | Konu | Karar |
|---|---|---|
| 006 | Frontend mimarisi | Çok-aşamalı; frontend/middle-end/backend katmanları |
| 007 | Analiz vs optimizasyon | Analiz yerinde işaretler; optimizasyon klonda dönüştürür |
| 008 | Optimizasyon konumu | Basitler AST'de, dataflow gerektirenler IR'de |
| 009 | Pass yönetimi | Fixpoint döngüsü, toggle'lı |
| 010 | Tip sistemi | Minimal+genişletilebilir Type; gizli dönüşüm yok; Error tipi |
| 011 | Scope/forward ref | Global'de forward ref, lokal'de declare-before-use (Java gibi) |
| 012 | Node hiyerarşisi | ExpressionNode / StatementNode ara tabanları |
| 013 | Analiz verisi yeri | Her şey AST'de; ref-count Symbol'da |
| 014 | Dil kapsamı | Pointer/class/generic yok; struct+array+tipli fonksiyon var |

179
docs/roadmap-frontend.md Normal file
View File

@ -0,0 +1,179 @@
# saQut Frontend Yol Haritası — Symbol Table + Semantic Analiz + Optimizasyon
> Bu belge, frontend'i tamamlamaya yönelik dosya-dosya uygulama planıdır.
> Kararların gerekçeleri: `docs/adr-frontend-analiz.md` (ADR-006…014).
> Tartışma akışı: `docs/transkript-frontend-tasarim.md`.
>
> **İlke:** Sıralama katıdır — her faz bir öncekine dayanır. Her faz sonunda
> mevcut örneklerle (`examples/Final.sqt`, `examples/source.sqt`) ve CLI
> komutlarıyla doğrulama yapılır; regresyon olmamalıdır. Kod temiz, anlaşılır ve
> yorum satırlarıyla takip edilebilir olmalıdır (header-only tarzı korunur,
> bkz. ADR-003).
---
## Genel Bakış
```
Faz 0 Temeller Type sınıfı + Diagnostic modülü + Hata kataloğu
Faz 1 AST Refactor ExpressionNode/StatementNode + analiz alanları
Faz 2 Symbol Table Symbol + Scope + SymbolTable + iki-geçişli toplama
Faz 3 Semantic Analiz Tip kontrolü + yapısal doğrulama (diagnostic'e basar)
Faz 4 Optimizasyon Pass manager (fixpoint) + constant folding + dead code
```
Katman eşlemesi (ADR-006):
- **Frontend:** Faz 03
- **Middle-end:** Faz 4
- **Backend:** bu yol haritasının dışında (C transpile → QBE → JIT, ADR-001)
---
## Faz 0 — Temeller (Type + Diagnostic + Hata Kataloğu)
**Bağımlılık:** yok. **Hedef:** her şeyin üstüne kurulacağı temel veri yapıları.
İlgili ADR: 010 (Type), 013 (Diagnostic).
### Dosyalar
| Dosya | İçerik |
|---|---|
| `src/core/type.hpp` | `Type` sınıfı. `enum class TypeKind { Primitive, Array, Struct, Function, Error }`. Primitif alt-tipleri: `int, float, double, char, string, bool, void`. Alanlar: array için `elementType`, function için `returnType`+`paramTypes`, struct için `structName`. Metotlar: `equals(const Type&)`, `toString()`, `toJson()`, factory'ler (`Type::primitive(...)`, `Type::array(...)`, `Type::error()`). |
| `src/diagnostic/diagnostic.hpp` | `enum class DiagLevel { Error, Warning, Note, Hint }`. `struct Diagnostic { DiagLevel level; std::string code; SourceLocation loc; std::string message; std::string hint; }`. **Hata kataloğu** (sabitler/enum): bkz. aşağı. |
| `src/diagnostic/diagnostic_engine.hpp` | `DiagnosticEngine`: `report(Diagnostic)`, `hasErrors()`, `errorCount()`, `printAll(std::ostream&)`, `toJson()`. Diagnostic'leri biriktirir; durdurma kararını pipeline verir (tüm hatalar toplanır, sonra raporlanır — ADR-013). |
### Hata Kataloğu (baştan belirlenir)
| Kod | Anlam | Hangi fazda üretilir |
|---|---|---|
| `E001` | Tanımsız değişken/isim | Faz 2/3 |
| `E002` | Aynı scope'ta çift tanım | Faz 2 |
| `E003` | Tip uyuşmazlığı | Faz 3 |
| `E004` | Döngü/switch dışı `break`/`continue` | Faz 3 |
| `E005` | Fonksiyon dışı `return` | Faz 3 |
| `E006` | Return tipi imzaya uymuyor | Faz 3 |
| `E007` | Tanımsız tip (bilinmeyen tip adı) | Faz 2/3 |
| `E008` | Fonksiyon çağrısı argüman sayısı/tipi uyuşmuyor | Faz 3 |
| `E009` | Array boyutu sabit değil / geçersiz | Faz 3 |
| `W001` | Kullanılmayan değişken | Faz 4 |
| `W002` | Sıfıra bölme (sabit folding) | Faz 4 |
| `W003` | Erişilemez (ölü) kod | Faz 4 |
*(Liste uygulamada genişleyebilir; yeni hatalar buraya eklenir.)*
### Doğrulama
- `Type::equals` / `toString` birim testleri.
- `DiagnosticEngine` topla → `printAll` çıktısı doğru sıralı.
---
## Faz 1 — AST Refactor (ExpressionNode / StatementNode + analiz alanları)
**Bağımlılık:** Faz 0 (Type). **Hedef:** node hiyerarşisini ifade/deyim olarak
ayır, analiz alanlarını ekle. İlgili ADR: 012, 013.
### Dosyalar
| Dosya | Değişiklik |
|---|---|
| `src/parser/ast_node.hpp` | İki ara taban ekle: `class ExpressionNode : public ASTNode { Type resolvedType; bool isConstant=false; /* foldedValue */ }` ve `class StatementNode : public ASTNode { bool isReachable=true; }`. |
| `src/parser/nodes/literal.hpp` · `binary_expr.hpp` · `identifier.hpp` · `expressions.hpp` (Call/Member/Index/Postfix) | Bu node'ları `ExpressionNode`'dan türet. |
| `src/parser/nodes/statements.hpp` (Block/If/While/For/DoWhile/Return/Break/Continue/ExpressionStatement) · `declarations.hpp`? | Statement'ları `StatementNode`'dan türet. (VariableDecl/FunctionDecl/StructDecl tartışmalı — declaration; şimdilik `StatementNode` veya doğrudan `ASTNode` altında değerlendirilir.) |
| `src/parser/nodes/identifier.hpp` | `IdentifierNode`'a `Symbol* resolvedSymbol = nullptr;` (Faz 2'de bağlanır). |
| `src/parser/nodes/*.cpp` | `toJson()`/`log()`'a yeni alanları (tip, isReachable) ekle — boş cpp'ler doluyor. |
### Doğrulama
- `saqut ast examples/Final.sqt` hâlâ geçerli JSON (regresyon yok, `python3 -m json.tool` ile).
- Derleme uyarısız (`-Wall -Wextra`).
---
## Faz 2 — Symbol Table (scope'lu, iki-geçişli toplama)
**Bağımlılık:** Faz 0, 1. **Hedef:** isim çözümleme + scope + referans toplama.
İlgili ADR: 011, 013.
### Dosyalar
| Dosya | İçerik |
|---|---|
| `src/symbol/symbol.hpp` | `enum class SymbolKind { Variable, Function, Parameter, Struct, Field }`. `struct Symbol { std::string name; SymbolKind kind; Type type; SourceLocation definitionLoc; std::vector<SourceLocation> references; Scope* scope; }`. |
| `src/symbol/scope.hpp` | `class Scope { Scope* parent; std::unordered_map<std::string, Symbol*> symbols; ... }`. `defineLocal()`, `lookupLocal()`. Her katman bir namespace. |
| `src/symbol/symbol_table.hpp` | Scope yığını yönetimi: `enterScope()`, `exitScope()`, `define(Symbol)` (aynı scope'ta duplicate → false + `E002`), `resolve(name)` (içten dışa), `addReference(name, loc)`, `getAllSymbols()`, `toJson()`. |
| `src/symbol/symbol_collector.hpp/.cpp` | **İki geçiş** (ADR-011): **Geçiş 1** → tüm üst-seviye tanımlarını (fonksiyon imzaları, struct isim+alan tipleri, global değişkenler) global scope'a hoist et. **Geçiş 2** → fonksiyon gövdelerine in; lokal'leri declare-before-use ile topla; her `IdentifierNode`'u `resolve()` edip `resolvedSymbol`'a bağla + `addReference`. |
### Notlar
- Scope oluşturan node'lar: Program, FunctionDecl (parametreler), Block, for/while.
- `src/json.hpp`'deki eski `collectSymbolsRecursive` bu sistemle değiştirilir.
- Tanımsız isim → `E001`; bilinmeyen tip → `E007`.
### Doğrulama
- `saqut symbols examples/Final.sqt` → zengin tablo (her sembolün tipi, tanım yeri,
referansları). Forward reference çalışır (sonra tanımlı fonksiyon çağrılabilir).
- Hatalı örnekler → `E001`/`E002`/`E007` diagnostic'leri.
---
## Faz 3 — Semantic Analiz (Tip Kontrolü + Yapısal Doğrulama)
**Bağımlılık:** Faz 2. **Hedef:** tipleri ata/kontrol et, yapısal kuralları
doğrula. İlgili ADR: 010, 013.
### Dosyalar
| Dosya | İçerik |
|---|---|
| `src/sema/type_checker.hpp/.cpp` | İfadeleri alttan üste gez, her `ExpressionNode`'a `resolvedType` ata. Gizli dönüşüm yok (ADR-010) → uyuşmazlıkta `E003`. Kontrol noktaları: atama (`=`), binary op operand tipleri, fonksiyon çağrısı argümanları (`E008`), array index, return değeri (`E006`), variable init tip uyumu. Hata olunca node'a `Type::error()` → ardışık sahte hata yok. |
| `src/sema/structural_validator.hpp/.cpp` | Parent pointer ile ağaç-tırmanma kontrolleri: `break`/`continue` döngü/switch içinde mi (`E004`), `return` fonksiyon içinde mi (`E005`), array boyutu sabit mi (`E009`). |
### Notlar
- Bu iki modül + Faz 2'nin collector'ı birlikte "semantic analiz" fazını oluşturur.
- Tüm hatalar toplanır, ilk hatada durulmaz; faz sonunda `DiagnosticEngine`
hepsini raporlar, pipeline durur (ADR-013).
### Doğrulama
- Doğru `.sqt` → temiz geçer, her expression node'unun tipi JSON'da görünür.
- Hatalı `.sqt` örnekleri → tüm semantik hatalar tek seferde listelenir.
---
## Faz 4 — Optimizasyon Framework
**Bağımlılık:** Faz 3. **Hedef:** opsiyonel, iteratif, toggle'lı kaynak-seviyesi
optimizasyon. **Orijinali bozmaz — klon üstünde** (ADR-007). İlgili ADR: 007, 008, 009.
### Dosyalar
| Dosya | İçerik |
|---|---|
| `src/core/config.hpp` | `CompilerConfig`: pass toggle'ları (`optConstantFolding`, `optDeadCodeElim`, …), `outputFormat`, `mode`, `optimized` bayrağı. |
| `src/opt/optimization_pass.hpp` | Soyut `OptimizationPass`: `virtual bool run(ASTNode* root, SymbolTable* table) = 0;` (değişiklik yaptıysa true). `name()`. |
| `src/opt/optimization_manager.hpp` | Pass listesi; `CompilerConfig`'e göre seçim; **fixpoint döngüsü** (hiçbir pass değişiklik yapmayana kadar). Çalışmadan önce AST'yi **klonlar** (`ASTNode::clone()` gerekebilir). |
| `src/opt/constant_folding.hpp/.cpp` | `BinaryExpression` operandları sabitse hesapla, sonucu sabit `Literal` ile değiştir (klonda). Tipe saygılı (`5/2`→int `2`, ADR-010). Sıfıra bölme → `W002`, katlama yapma. |
| `src/opt/dead_code_elim.hpp/.cpp` | `isReachable` (Faz 3) işaretine göre: `return`/`break`/`continue` sonrası statement'lar, `if(false)`, sıfır-referanslı değişken (`W001`/`W003`). |
### CLI Entegrasyonu
| Dosya | Değişiklik |
|---|---|
| `src/cli/args.hpp` | `--optimized` ve `--opt-all`/`--opt-none`/`--skip-*` bayrakları`CompilerConfig`. |
| `src/cli/commands/ast.hpp` · `symbols.hpp` | `--optimized` verilince klon+optimize edilmiş hali göster; verilmezse orijinal. |
### Doğrulama
- `saqut ast file --optimized``1+2` katlanmış, ölü kod elenmiş.
- `saqut ast file` (bayraksız) → orijinal, **değişmemiş** (öncesi/sonrası ayrımı).
- Fixpoint: zincirleme optimizasyon (folding → yeni dead code → DCE) yakalanır.
---
## Tamamlanınca
Bu yol haritası bittiğinde frontend tamamlanmış olur:
- Parser → analizli, tipli, sembol-çözümlü AST + zengin symbol table.
- Tam hata raporlama (toplu, kataloglu).
- Opsiyonel, incelenebilir optimizasyon.
Sonraki adım (ayrı yol haritası): **IR güçlendirme** (kontrol akışı/fonksiyon/
bellek opcode'ları, ADR-005/Issue 5.1) → **C transpile backend** → QBE → JIT
(ADR-001 sırası). Dinamik array'in runtime bellek modeli (ADR-014) backend
fazında kararlaştırılır.

View File

@ -0,0 +1,214 @@
# 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ı)