From 5e0c3eeb7e2bddd634cd8b433a67f96e426f36b2 Mon Sep 17 00:00:00 2001 From: saqut Date: Sun, 14 Jun 2026 17:56:42 +0300 Subject: [PATCH] docs: frontend tasarim kararlari, transkript ve yol haritasi MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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). --- docs/adr-frontend-analiz.md | 389 ++++++++++++++++++++++++++++ docs/roadmap-frontend.md | 179 +++++++++++++ docs/transkript-frontend-tasarim.md | 214 +++++++++++++++ 3 files changed, 782 insertions(+) create mode 100644 docs/adr-frontend-analiz.md create mode 100644 docs/roadmap-frontend.md create mode 100644 docs/transkript-frontend-tasarim.md diff --git a/docs/adr-frontend-analiz.md b/docs/adr-frontend-analiz.md new file mode 100644 index 0000000..f0194fb --- /dev/null +++ b/docs/adr-frontend-analiz.md @@ -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:** açı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` → 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 (``) | ❌ 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 | diff --git a/docs/roadmap-frontend.md b/docs/roadmap-frontend.md new file mode 100644 index 0000000..05479ca --- /dev/null +++ b/docs/roadmap-frontend.md @@ -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 0–3 +- **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 references; Scope* scope; }`. | +| `src/symbol/scope.hpp` | `class Scope { Scope* parent; std::unordered_map 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. diff --git a/docs/transkript-frontend-tasarim.md b/docs/transkript-frontend-tasarim.md new file mode 100644 index 0000000..dade0dd --- /dev/null +++ b/docs/transkript-frontend-tasarim.md @@ -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 +(``) 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ı)