Compare commits
39 Commits
license-su
...
master
| Author | SHA1 | Date |
|---|---|---|
|
|
d51e48dbb2 | |
|
|
3d5912f991 | |
|
|
044655d675 | |
|
|
b6e4d75bde | |
|
|
580f9f8c5f | |
|
|
539e08e521 | |
|
|
a4bd5110b3 | |
|
|
7c7f886e9c | |
|
|
9b486942c0 | |
|
|
8453dcd796 | |
|
|
2b9888c021 | |
|
|
aa4d9fb8c2 | |
|
|
e84828b631 | |
|
|
b071dab54c | |
|
|
dc41a5df59 | |
|
|
e223995f20 | |
|
|
ba08bf4511 | |
|
|
ea5bcee490 | |
|
|
6b5a67343b | |
|
|
f3358473ac | |
|
|
63ad3e2dcf | |
|
|
cbc7a3ddb5 | |
|
|
e2a82f9bb9 | |
|
|
dae55d5907 | |
|
|
df5bb6187f | |
|
|
1859cea9a1 | |
|
|
1232748f9f | |
|
|
cf6f706449 | |
|
|
f7467f18b3 | |
|
|
b986f0cb26 | |
|
|
7c5a86c39b | |
|
|
3f6c2ee4c9 | |
|
|
93cbc7db3a | |
|
|
0a15cfe706 | |
|
|
0fbe742de0 | |
|
|
12356d8a73 | |
|
|
6948384134 | |
|
|
d9db17c732 | |
|
|
d4d912f01e |
|
|
@ -9,3 +9,14 @@ saqut
|
||||||
*.swp
|
*.swp
|
||||||
*.swo
|
*.swo
|
||||||
*~
|
*~
|
||||||
|
|
||||||
|
# Parola içerebilen yerel API istemcisi - asla commit edilmez
|
||||||
|
scripts/gitea.py
|
||||||
|
|
||||||
|
# Python bytecode cache
|
||||||
|
__pycache__/
|
||||||
|
|
||||||
|
# CMake versiyona özgü dosyalar (otomatik üretilir, her güncellemede değişir)
|
||||||
|
build/CMakeFiles/[0-9]*/
|
||||||
|
build/.cmake/
|
||||||
|
build/compile_commands.json
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,102 @@
|
||||||
|
# CLAUDE.md — saQut Proje Bağlamı
|
||||||
|
|
||||||
|
> Bu dosya her oturumda yüklenir. Amaç: projenin kimliğini, kilitli kararları,
|
||||||
|
> mevcut durumu ve çalışma konvansiyonlarını hızlıca hatırlatmak.
|
||||||
|
|
||||||
|
## İletişim
|
||||||
|
- **Kullanıcıyla TÜM yazışmalar Türkçe.** (Sahibi: Abdussamed ULUTAŞ.)
|
||||||
|
|
||||||
|
## Proje nedir?
|
||||||
|
saQut, **programlanabilir ve incelenebilir bir derleyici** — bir "alet çantası"
|
||||||
|
(toolbox). Asıl varlık sebebi dilin kendisi değil, **derleme sürecinin her
|
||||||
|
aşamasının dışarıdan görülebilir/müdahale edilebilir olması**: token'lar, AST,
|
||||||
|
sembol tablosu, optimizasyon öncesi/sonrası ve IR ayrı ayrı incelenebilir.
|
||||||
|
Uygulama dili **C++** (header-only eğilimli, ADR-003). CMake + Ninja. `build/`
|
||||||
|
git'te izlenir.
|
||||||
|
|
||||||
|
## Kilitli kararlar (değiştirme — gerekçeler ADR'lerde)
|
||||||
|
- **Çalıştırma modeli: IR + bytecode VM (yorumlayıcı döngü).** Tree-walker DEĞİL,
|
||||||
|
gerçek makine-kodu JIT DEĞİL (kapsam dışı; öncelik determinizm + incelenebilirlik,
|
||||||
|
ham hız değil). C'ye transpile ileride geçerli 2. backend. İleride makine kodu
|
||||||
|
gerekirse libgccjit/LLVM'e bağlanılır (çok uzak). Bellek = host C++ heap; özel
|
||||||
|
allocator yok. (ADR-015)
|
||||||
|
- **Dil kimliği:** prosedürel, C-ailesi sözdizimi, value semantics, zorunlu
|
||||||
|
class/main boilerplate yok. **Yok:** class/OOP, closure, generic, kullanıcı
|
||||||
|
pointer'ı (`*`/`&`), auto/tip çıkarımı, gizli int↔float (tek istisna sabit
|
||||||
|
folding). **Var:** struct, tipli fonksiyonlar, array (`int[]`). `interface`
|
||||||
|
**ertelendi** (reddedilmedi, ADR-018).
|
||||||
|
- **Analiz vs Optimizasyon:** Analiz orijinal AST üstünde annotation; optimizasyon
|
||||||
|
**klon** üstünde dönüşüm. `ASTNode::clone()` yük taşıyan merkezi bileşen
|
||||||
|
(parent pointer'lar + sembol tablosu remap edilir, ADR-007). Fixpoint döngüsü +
|
||||||
|
iterasyon tavanı (`maxFixpointRounds`, ADR-009).
|
||||||
|
- **Literal/tip kuralı:** tamsayı literali bağlama-göre tiplenir (`float x = 1;`
|
||||||
|
geçerli; `int y = 1.5;`→E003; değişken→değişken gizli dönüşüm yok). Döngüsel
|
||||||
|
by-value struct → E010. (ADR-010/011)
|
||||||
|
- **FFI seam:** kasıtlı "host fonksiyonu çağır" mekanizması (`callhost`); `print`
|
||||||
|
ilk müşteri (ADR-016). Batteries = sınır/FFI problemi, "zlib'i yeniden yaz"
|
||||||
|
değil; kripto asla elle yazılmaz (ADR-017).
|
||||||
|
|
||||||
|
## Mevcut durum (yapılan vs planlanan)
|
||||||
|
- **Çalışıyor:** lexer, tokenizer, Pratt parser, AST, AST'nin JSON serileştirmesi,
|
||||||
|
CLI iskeleti (`tokens`/`ast`/`symbols`/`run`), konum takibi, basit aritmetiği
|
||||||
|
düşüren minimal IR deneyi.
|
||||||
|
- **Planlı (henüz YOK):** sembol tablosu, semantik analiz, tip sistemi, diagnostic
|
||||||
|
motoru, optimizasyon, IR+bytecode VM ile çalıştırma.
|
||||||
|
- **Birinci kilometre taşı ("bitti" tanımı):** `examples/fibonacci.sqt`
|
||||||
|
(recursive + iterative) derlenip çalıştırılabilmeli.
|
||||||
|
- **İlke:** Önce uçtan uca tek **dikey dilim**, sonra çerçeve. Erken soyutlamadan kaçın.
|
||||||
|
|
||||||
|
## Belge haritası
|
||||||
|
- `readme.md` — toolbox çerçevesi, built-vs-planned, dil kimliği, çalıştırma modeli.
|
||||||
|
- `docs/fikirler.md` — ADR-001…005 (backend stratejisi, parser, header-only, token, IR).
|
||||||
|
- `docs/adr-frontend-analiz.md` — ADR-006…019 (frontend, analiz/optimizasyon,
|
||||||
|
çalıştırma modeli, FFI, interface, bellek).
|
||||||
|
- `docs/roadmap-frontend.md` — faz-faz uygulama planı (Faz 0–4 → fibonacci).
|
||||||
|
- `docs/transkript-frontend-tasarim.md` — tasarım oturumu transkripti.
|
||||||
|
- `examples/fibonacci.sqt` — geçerli referans program.
|
||||||
|
- `examples/parser-stress/` — yalnızca parser'ı zorlayan, **geçerli olmayan** fixture'lar.
|
||||||
|
|
||||||
|
## Gitea issue yönetimi
|
||||||
|
- Repo: `git.saqut.com/saqut/saqut-compiler` (Gitea). Cloudflare, Python-urllib
|
||||||
|
User-Agent'ını banlıyor → tarayıcı User-Agent'ı şart.
|
||||||
|
- `scripts/gitea.py` — API istemcisi (`~/.git-credentials`'tan kimlik okur).
|
||||||
|
Komutlar: `list/get/create/edit/comment`. Toplu issue üretimi:
|
||||||
|
`scripts/create_issues.py`, `create_future_issues.py`, `create_syntax_test_issues.py`.
|
||||||
|
- **Issue yapısı (bu oturumda kuruldu):**
|
||||||
|
- **#69–73** `faz-plani` — Faz 0–4 (Tip+Diagnostic, AST refactor, Symbol Table,
|
||||||
|
Semantik Analiz, Optimizasyon). Format: Giriş/Gelişme/Sonuç-Başarı Kriterleri +
|
||||||
|
mühendis-olmayan analiz.
|
||||||
|
- **#74–98, #111** `fikir` — IR/VM tasarımı, modül/import, tip genişletmeleri
|
||||||
|
(decimal/date/enum/string), FFI/builtin/stdlib, tooling (LSP/highlight/fmt),
|
||||||
|
gelecek vizyonu (time-travel debug, WASM playground, test bloğu, paket yöneticisi).
|
||||||
|
Format: Giriş/Gelişme/**Açık Sorular** (başarı kriteri YOK).
|
||||||
|
- **#99–105** `test-senaryosu` — kaynak kod + beklenen çıktı içeren golden-test'ler.
|
||||||
|
- **#106–110** `cli-ux`/`kalite-mimari` — CLI fikirleri, C/Java/Go tarzı tavsiyeler.
|
||||||
|
- LSP (#91) ve CLI (#107) **Tier 0–4** katmanlı yetenek haritası olarak yazıldı;
|
||||||
|
#111 ekosistem bağımlılık sırası.
|
||||||
|
|
||||||
|
## Lisans (LICENSE.md — bu oturumda yeniden yazıldı)
|
||||||
|
- **Model:** "Kaynağı Açık — Ticari Kullanımı Kısıtlı" (açık kaynak ama **özgür
|
||||||
|
yazılım değil**). Önceki GPL-tarzı copyleft metin niyetle çelişiyordu, değiştirildi.
|
||||||
|
- **Çekirdek ilke:** Gelir saQut'un **ürettiği Çıktıdan** elde edilir, **saQut'un
|
||||||
|
kendisinden değil.**
|
||||||
|
- **Serbest (ticari dahil):** kod yazmak/derlemek, üretilen programları/exe/işlenmiş
|
||||||
|
veriyi satmak, derleyiciyi özel araç olarak iç kullanım.
|
||||||
|
- **İzin gerektirir (telif sahibinden):** derleyiciyi 3. tarafa kurup ücret almak,
|
||||||
|
Web-IDE/derleme servisi, canlı backend motoru, otomasyon/AI aracı, alt-bileşen
|
||||||
|
gömme, iç parçaları (AST/optimizatör) ticari yeniden kullanım. Runtime'a bağımlı
|
||||||
|
sürümlerde sunucu-tarafı ticari kullanım da bu kapsamda.
|
||||||
|
- **Maddeler:** §7 Katkı (PR herkese açık, **merge kararı yalnızca Abdussamed
|
||||||
|
ULUTAŞ**; katkı sahipleri ticari lisanslama dâhil hakları telif sahibine verir),
|
||||||
|
§8 Patent (lisans + dava açana otomatik fesih), §9 Marka ("saQut" adı korunur),
|
||||||
|
§11 Fesih (ihlalde otomatik + 30 gün düzeltme; edinilmiş Çıktı korunur).
|
||||||
|
- Üç dilde (TR/EN/DE), TR esas. Ticari lisans iletişimi: saqutsoftware+gitea@gmail.com
|
||||||
|
- **Not:** Bespoke lisans; ciddi ticari aşamada hukukçu gözden geçirmesi önerilir.
|
||||||
|
Telif yalnızca kodu/belgeyi korur, **fikri/tasarımı değil** (sıfırdan yeniden
|
||||||
|
yazım engellenemez); isim ise marka ile korunur.
|
||||||
|
|
||||||
|
## Çalışma konvansiyonları
|
||||||
|
- Commit mesajları sonunda: `Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>`.
|
||||||
|
- Ana dal `master`; commit/push kullanıcı isteyince yapılır.
|
||||||
|
- Wiki API'si Gitea'da REST üzerinden çalışmadı; wiki içeriği `wiki.md`'ye yazılıp
|
||||||
|
kullanıcı tarafından elle yapıştırılıyor.
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
cmake_minimum_required(VERSION 3.16)
|
||||||
|
project(saqut VERSION 0.1 LANGUAGES CXX)
|
||||||
|
|
||||||
|
set(CMAKE_CXX_STANDARD 20)
|
||||||
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
|
|
||||||
|
add_compile_options(-Wall -Wextra)
|
||||||
|
|
||||||
|
if(CMAKE_BUILD_TYPE STREQUAL "Debug" OR NOT CMAKE_BUILD_TYPE)
|
||||||
|
add_compile_options(-g -O0)
|
||||||
|
message(STATUS "Debug modu aktif")
|
||||||
|
else()
|
||||||
|
add_compile_options(-O3)
|
||||||
|
message(STATUS "Release modu aktif")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Tüm kaynak dosyaları topla
|
||||||
|
file(GLOB_RECURSE SOURCES "src/*.cpp")
|
||||||
|
|
||||||
|
add_executable(saqut ${SOURCES})
|
||||||
|
|
||||||
|
target_include_directories(saqut PRIVATE src)
|
||||||
|
|
@ -0,0 +1,234 @@
|
||||||
|
## Lisans / License / Lizenz
|
||||||
|
|
||||||
|
[Türkçe Sürüm](#türkçe-lisans-sürümü) | [English Version](#english-license-version) | [Deutsche Version](#deutsche-lizenzversion)
|
||||||
|
|
||||||
|
> Bu lisans üç dilde sunulmuştur. Yorum farklılıklarında **Türkçe sürüm esas alınır.**
|
||||||
|
> This license is provided in three languages. In case of any discrepancy, the **Turkish version prevails.**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Türkçe Lisans Sürümü
|
||||||
|
|
||||||
|
# saQut Lisansı (Sürüm 1.0)
|
||||||
|
**Kaynağı Açık — Ticari Kullanımı Kısıtlı Lisans**
|
||||||
|
|
||||||
|
Telif Hakkı (c) 2026, Abdussamed ULUTAŞ (ve saQut Katkıda Bulunanları). Tüm hakları saklıdır.
|
||||||
|
|
||||||
|
Bu lisans; saQut derleyicisini, kaynak kodlarını, derleme betiklerini (build scripts), tüm backend bileşenlerini (yorumlayıcı, bytecode VM, derleyici ve transpiler) ve beraberindeki dokümantasyon ile mimari tasarımı (ADR belgeleri dâhil) — toplu olarak "Yazılım" olarak anılacaktır — kapsar.
|
||||||
|
|
||||||
|
saQut açık kaynaklı (kaynağı görülebilir) bir projedir; ancak **özgür yazılım (free/libre software) değildir.** Yazılımın incelenmesi ve değiştirilmesi serbestken, ticari kullanımı işbu lisansın koşullarına ve telif hakkı sahibinin iznine tabidir.
|
||||||
|
|
||||||
|
#### 1. Temel Felsefe (Alet Çantası Kuralı)
|
||||||
|
saQut; programlanabilir ve incelenebilir bir derleyici alet çantası (toolbox) olarak tasarlanmıştır. Varlık sebebi, derleme sürecinin her aşamasının — token'lar, Soyut Sözdizimi Ağacı (AST), sembol tabloları ve Ara Temsil (IR) — dışarıdan şeffaf ve görülebilir kalmasıdır. Yazılımın her türlü yeniden dağıtımı ve üzerinde yapılacak değişiklikler bu şeffaflık ilkesini korumakla yükümlüdür.
|
||||||
|
|
||||||
|
#### 2. Tanımlar
|
||||||
|
- **Yazılım:** Yukarıda tanımlanan saQut derleyicisi ve tüm bileşenleri (kaynak kod, yorumlayıcı, bytecode VM, derleyici ve transpiler backend'leri, dokümantasyon ve mimari tasarım).
|
||||||
|
- **Çıktı:** Yazılım kullanılarak üretilen her şey; saQut dilinde yazılmış kaynak kodlar (`.sqt` dosyaları), token/AST/sembol tablosu dökümleri, üretilen bytecode, transpile edilmiş kaynak kod, bağımsız çalıştırılabilir dosyalar ve Yazılımın işlediği verilerden elde edilen sonuçlar.
|
||||||
|
- **Ticari Kullanım:** Yazılımın kendisinden doğrudan veya dolaylı olarak gelir elde edilmesi; Yazılımın bir ürün, hizmet, motor, bileşen veya otomasyon aracı olarak Üçüncü Taraflara sunulması, kurulması ya da onlar adına işletilmesi.
|
||||||
|
- **Üçüncü Taraf:** Yazılımı kullanan kişi veya kurum dışında kalan müşteri, son kullanıcı ya da diğer taraflar.
|
||||||
|
|
||||||
|
#### 3. İnceleme, Değiştirme ve Ticari Olmayan Kullanım İzni
|
||||||
|
İşbu lisans kapsamında herkese, ücretsiz olarak aşağıdaki haklar tanınır:
|
||||||
|
- Yazılımı kişisel, eğitsel ve ticari olmayan amaçlarla kullanmak, incelemek ve çalıştırmak.
|
||||||
|
- Yazılımın kaynak kodunu incelemek ve değiştirmek.
|
||||||
|
- Değiştirilmiş veya değiştirilmemiş kopyaları ticari olmayan koşullarda dağıtmak. Ancak dağıtılan her kopya; (a) işbu lisansa tabi kalmalı, (b) kaynağı açık (source-available) kalmalı ve (c) aşağıdaki Atıf Şartı'na uymalıdır.
|
||||||
|
|
||||||
|
#### 4. Çıktı ve Sonuç İstisnası (KRİTİK)
|
||||||
|
Yazılımı kullanan geliştiricilerin özgürlüğünü güvence altına almak amacıyla:
|
||||||
|
- **Çıktı sizindir.** Bölüm 2'de tanımlanan tüm Çıktı, işbu lisansın kısıtlamalarına **tabi değildir.** Geliştiriciler kendi Çıktılarını diledikleri gibi kullanma, kapatma (kapalı kaynak yapma), lisanslama ve **ticari olarak satma** hakkına tam olarak sahiptir.
|
||||||
|
- **Gelir, Çıktıdan elde edilir.** Yazılımı kendi makinenizde (kişisel veya kurumsal) özel bir araç olarak kullanıp ürettiğiniz programları, bağımsız çalıştırılabilir dosyaları veya işlenmiş verileri (örneğin bir Excel raporu veya bir veritabanı sonucu) satmanız serbesttir. Bu durumda Üçüncü Taraf, Yazılımı değil yalnızca sonucu alır.
|
||||||
|
|
||||||
|
#### 5. Ticari Kısıtlamalar
|
||||||
|
Aşağıdaki kullanımlar, Yazılımın bizzat kendisinden gelir elde edilmesi anlamına geldiğinden, telif hakkı sahibinin önceden yazılı izni olmaksızın yapılamaz:
|
||||||
|
- Yazılımı bir Üçüncü Taraf'ın sunucusuna veya cihazına kurup çalıştırılması karşılığında ücret almak (örneğin bir müşteri için `saqut run yedek.sql` komutunu çalıştırıp ücret talep etmek).
|
||||||
|
- Yazılımı çevrimiçi bir hizmet olarak sunmak (Web tabanlı IDE, çevrimiçi derleme veya çalıştırma servisi).
|
||||||
|
- Yazılımı, Üçüncü Tarafların canlı istekleriyle işleyen ticari bir hizmetin backend'i veya motoru hâline getirmek.
|
||||||
|
- Yazılımı ticari bir otomasyon/iş hattının (pipeline) aracı olarak işletmek ya da bir yapay zekâ sistemine araç olarak sağlamak.
|
||||||
|
- Yazılımı ticari bir ürünün alt bileşeni olarak gömmek (embed).
|
||||||
|
- Yazılımın iç bileşenlerini veya mimari tasarımını (örneğin AST görüntüleme veya optimizasyon algoritmaları) ticari bir özellik ya da ürün olarak yeniden kullanmak.
|
||||||
|
|
||||||
|
**Sunucu ve sürüm notu:** Yazılım çalışmak için saQut çalışma zamanına (yorumlayıcı / bytecode VM) ihtiyaç duyduğu sürece, sunucu tarafındaki her ticari kullanım fiilen Yazılımı sunucuya yerleştirmek anlamına gelir ve bu nedenle yukarıdaki kısıtlamalara tabidir. Yazılımın, saQut çalışma zamanına ihtiyaç duymayan bağımsız çalıştırılabilir dosyalar üreten backend'i kullanıma sunulduğunda; bu tür bağımsız Çıktıların sunucuda ticari olarak çalıştırılması Bölüm 4 kapsamında serbesttir.
|
||||||
|
|
||||||
|
#### 6. İzin ve Ticari Lisans
|
||||||
|
Bölüm 5'te sayılan kısıtlamaların tamamı yalnızca telif hakkı sahibine aittir ve istisna tanıma yetkisi münhasıran ona aittir. Bu kullanımlardan herhangi birini gerçekleştirmek isteyen kişi veya kurum, telif hakkı sahibinden (Abdussamed ULUTAŞ) önceden yazılı izin talep edebilir; izin verildiği takdirde ilgili kullanım serbest hâle gelir.
|
||||||
|
İletişim (ticari lisans talepleri): saqutsoftware+gitea@gmail.com
|
||||||
|
|
||||||
|
#### 7. Katkılar (Contributions)
|
||||||
|
saQut'a katkı herkese açık bir öneri sürecidir; ancak nihai karar münhasıran telif hakkı sahibine aittir.
|
||||||
|
- Herhangi biri değişiklik önerisi (Pull Request / PR) açabilir. Bir katkının projeye dâhil edilip edilmeyeceğine ve birleştirilip (merge) birleştirilmeyeceğine **yalnızca Abdussamed ULUTAŞ karar verir.** Hiç kimsenin, bir katkısını projeye dâhil ettirme yönünde bir hakkı yoktur.
|
||||||
|
- Bir katkıyı (kod, belge, tasarım veya başka bir materyal) gönderen kişi, bu katkıyı göndermekle telif hakkı sahibine (Abdussamed ULUTAŞ); katkı üzerinde **dünya çapında, süresiz, geri alınamaz, ücretsiz ve alt-lisans verilebilir** bir kullanma, çoğaltma, değiştirme, dağıtma ve **ticari olarak lisanslama** hakkı tanır. Bu hak, telif hakkı sahibinin katkıyı içeren Yazılımı Bölüm 6 kapsamında ticari olarak lisanslayabilmesini güvence altına alır.
|
||||||
|
- Katkıda bulunan; gönderdiği materyalin kendi eseri olduğunu veya gerekli haklara sahip bulunduğunu ve bu izni vermeye yetkili olduğunu beyan eder. Katkıda bulunanlar, kendi katkıları üzerindeki telif haklarını korur; yukarıdaki izin bu haklara ek olarak verilir.
|
||||||
|
|
||||||
|
#### 8. Patent Hakları
|
||||||
|
- **Patent lisansı:** İşbu lisans kapsamında Yazılımı kullanma hakkına sahip olan her kişiye; telif hakkı sahibi ve katkıda bulunanlar tarafından sahip olunan ve Yazılımın bu lisansça izin verilen biçimde kullanılması için zorunlu olarak ihlal edilen patent istemleri bakımından, ücretsiz ve (aşağıdaki fesih hükmü saklı kalmak kaydıyla) geri alınamaz bir patent lisansı tanınır.
|
||||||
|
- **Patent misillemesi (otomatik fesih):** Eğer bir kişi veya kurum, Yazılımın bu lisansça izin verilen kullanımının bir patenti ihlal ettiğini ileri sürerek telif hakkı sahibine veya herhangi bir katkıda bulunana karşı **patent davası** açarsa, işbu lisans kapsamında o kişiye/kuruma tanınan tüm haklar (patent lisansı dâhil) **otomatik olarak sona erer.**
|
||||||
|
|
||||||
|
#### 9. Marka ve İsim Hakları
|
||||||
|
İşbu lisans; "saQut" adı, logosu veya ilgili marka unsurları üzerinde **hiçbir hak tanımaz.** Özellikle:
|
||||||
|
- Yazılımın değiştirilmiş bir sürümünü dağıtırsanız, bu sürümü "saQut" adıyla veya bu adı çağrıştıracak ya da karıştırılmaya yol açabilecek bir adla adlandıramazsınız.
|
||||||
|
- Telif hakkı sahibinin önceden yazılı izni olmadan, "saQut" adını ürünlerinizde, hizmetlerinizde veya tanıtımlarınızda bir onay/destek ima edecek biçimde kullanamazsınız.
|
||||||
|
- Yazılımın kaynağına ve telif hakkı sahibine yapılan dürüst ve tanımlayıcı atıflar (örneğin "saQut tabanlıdır") bu maddenin kapsamı dışındadır.
|
||||||
|
|
||||||
|
#### 10. Atıf Şartı
|
||||||
|
Orijinal telif hakkı bildirimi ile işbu izin bildirimi, Yazılımın tüm kopyalarına veya önemli parçalarına dâhil edilmelidir.
|
||||||
|
|
||||||
|
#### 11. Fesih (Termination)
|
||||||
|
- İşbu lisansın koşullarından herhangi birini ihlal etmeniz hâlinde, lisans kapsamında size tanınan tüm haklar **otomatik olarak sona erer.**
|
||||||
|
- İhlaliniz düzeltilebilir nitelikteyse ve ihlali öğrendikten sonra **30 (otuz) gün** içinde giderirseniz — telif hakkı sahibi bu süre içinde lisansı açıkça feshetmemiş olmak kaydıyla — haklarınız yeniden yürürlüğe girer. Aynı kişinin tekrarlayan ihlallerinde bu düzeltme imkânı uygulanmaz.
|
||||||
|
- Haklarınızın sona ermesi, daha önce işbu lisansa uygun olarak edindiğiniz Çıktı (Bölüm 4) üzerindeki haklarınızı etkilemez.
|
||||||
|
- Garanti reddi ve sorumluluk sınırlamaları, fesihten sonra da yürürlükte kalmaya devam eder.
|
||||||
|
|
||||||
|
#### 12. Garanti Reddi
|
||||||
|
YAZILIM "OLDUĞU GİBİ" SUNULMAKTADIR; TİCARETE UYGUNLUK, BELİRLİ BİR AMACA UYGUNLUK VE İHLAL ETMEME GARANTİLERİ DAHİL ANCAK BUNLARLA SINIRLI OLMAMAK ÜZERE, AÇIK VEYA ZIMNİ HİÇBİR GARANTİ VERİLMEMEKTEDİR. YAZARLAR VEYA TELİF HAKKI SAHİPLERİ, YAZILIMLA VEYA YAZILIMIN KULLANIMIYLA YA DA DİĞER İŞLEMLERLE BAĞLANTILI OLARAK ORTAYA ÇIKAN SÖZLEŞME, HAKSIZ FİİL VEYA DİĞER DURUMLARDAKİ HİÇBİR TALEP, TAZMİNAT VEYA DİĞER YÜKÜMLÜLÜKLERDEN SORUMLU TUTULAMAZ.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### English License Version
|
||||||
|
|
||||||
|
# The saQut License (Version 1.0)
|
||||||
|
**Source-Available — Commercial-Use Restricted License**
|
||||||
|
|
||||||
|
Copyright (c) 2026, Abdussamed ULUTAŞ (and saQut Contributors). All rights reserved.
|
||||||
|
|
||||||
|
This license applies to the saQut compiler, its source code, build scripts, all backend components (the interpreter, the bytecode VM, the compiler, and the transpiler), and the accompanying documentation and architectural design (including the ADR documents) — collectively referred to as "the Software".
|
||||||
|
|
||||||
|
saQut is an open-source (source-available) project; however, it is **not free/libre software.** While inspecting and modifying the Software is permitted, its commercial use is subject to the terms of this license and to the permission of the copyright holder.
|
||||||
|
|
||||||
|
#### 1. Core Philosophy (The Toolbox Rule)
|
||||||
|
saQut is designed as a programmable, inspectable compiler toolbox. Its reason for existence is to keep every stage of the compilation process — tokens, the Abstract Syntax Tree (AST), symbol tables, and the Intermediate Representation (IR) — transparent and observable from the outside. Any redistribution or modification of the Software must preserve this principle of transparency.
|
||||||
|
|
||||||
|
#### 2. Definitions
|
||||||
|
- **The Software:** The saQut compiler and all of its components as defined above (source code, interpreter, bytecode VM, compiler and transpiler backends, documentation, and architectural design).
|
||||||
|
- **Output:** Anything produced using the Software; source code written in the saQut language (`.sqt` files), token/AST/symbol-table dumps, generated bytecode, transpiled source code, standalone executables, and any results derived from data processed by the Software.
|
||||||
|
- **Commercial Use:** Deriving revenue, directly or indirectly, from the Software itself; offering, deploying, or operating the Software as a product, service, engine, component, or automation tool for or on behalf of Third Parties.
|
||||||
|
- **Third Party:** Any customer, end user, or other party other than the person or organization using the Software.
|
||||||
|
|
||||||
|
#### 3. Permission to Inspect, Modify, and Use Non-Commercially
|
||||||
|
This license grants everyone, free of charge, the following rights:
|
||||||
|
- To use, inspect, and run the Software for personal, educational, and non-commercial purposes.
|
||||||
|
- To inspect and modify the source code of the Software.
|
||||||
|
- To distribute modified or unmodified copies under non-commercial terms, provided that each distributed copy (a) remains under this license, (b) remains source-available, and (c) complies with the Attribution Requirement below.
|
||||||
|
|
||||||
|
#### 4. The Output & Results Exception (CRITICAL)
|
||||||
|
To safeguard the freedom of developers using the Software:
|
||||||
|
- **Your Output is yours.** All Output as defined in Section 2 is **not** subject to the restrictions of this license. Developers retain the full right to use, close (make proprietary), license, and **sell commercially** their own Output.
|
||||||
|
- **Revenue is earned from the Output.** You are free to use the Software as a private tool on your own machine (personal or corporate) and to sell the programs, standalone executables, or processed data you produce (for example, an Excel report or a database result). In such cases the Third Party receives only the result, not the Software.
|
||||||
|
|
||||||
|
#### 5. Commercial Restrictions
|
||||||
|
Because the following uses amount to deriving revenue from the Software itself, they may not be carried out without the prior written permission of the copyright holder:
|
||||||
|
- Installing the Software on a Third Party's server or device and charging for its execution (for example, running `saqut run backup.sql` for a customer for a fee).
|
||||||
|
- Offering the Software as an online service (a web-based IDE, or an online compilation or execution service).
|
||||||
|
- Making the Software the backend or engine of a commercial service that is driven by the live requests of Third Parties.
|
||||||
|
- Operating the Software as a tool within a commercial automation pipeline, or providing it as a tool to an artificial-intelligence system.
|
||||||
|
- Embedding the Software as a sub-component of a commercial product.
|
||||||
|
- Repurposing the internal components or architectural design of the Software (for example, its AST visualization or optimization algorithms) as a commercial feature or product.
|
||||||
|
|
||||||
|
**Server and version note:** As long as the Software requires the saQut runtime (the interpreter / bytecode VM) in order to run, any commercial server-side use effectively places the Software on the server and is therefore subject to the restrictions above. Once a backend that produces standalone executables not requiring the saQut runtime becomes available, running such standalone Output commercially on a server is permitted under Section 4.
|
||||||
|
|
||||||
|
#### 6. Permission and Commercial Licensing
|
||||||
|
All restrictions listed in Section 5 belong solely to the copyright holder, who has the exclusive authority to grant exceptions. Any person or organization wishing to carry out any of these uses may request prior written permission from the copyright holder (Abdussamed ULUTAŞ); once granted, the relevant use becomes permitted.
|
||||||
|
Contact (commercial-license requests): saqutsoftware+gitea@gmail.com
|
||||||
|
|
||||||
|
#### 7. Contributions
|
||||||
|
Contributing to saQut is an open proposal process; however, the final decision rests solely with the copyright holder.
|
||||||
|
- Anyone may open a change proposal (a Pull Request / PR). Whether a contribution is included in the project and whether it is merged is decided **solely by Abdussamed ULUTAŞ.** No one has any right to have their contribution included in the project.
|
||||||
|
- By submitting a contribution (code, documentation, design, or other material), the contributor grants the copyright holder (Abdussamed ULUTAŞ) a **worldwide, perpetual, irrevocable, royalty-free, and sublicensable** right to use, reproduce, modify, distribute, and **license commercially** that contribution. This right ensures that the copyright holder may license the Software including the contribution on a commercial basis under Section 6.
|
||||||
|
- The contributor represents that the submitted material is their own work or that they hold the necessary rights, and that they are authorized to grant this permission. Contributors retain copyright in their own contributions; the permission above is granted in addition to those rights.
|
||||||
|
|
||||||
|
#### 8. Patent Rights
|
||||||
|
- **Patent license:** Subject to this license, each person who has the right to use the Software is granted a royalty-free and (except as stated in the termination provision below) irrevocable patent license, under those patent claims owned by the copyright holder and contributors that are necessarily infringed by using the Software in the manner permitted by this license.
|
||||||
|
- **Patent retaliation (automatic termination):** If any person or entity initiates **patent litigation** against the copyright holder or any contributor, alleging that the use of the Software as permitted by this license infringes a patent, then all rights granted to that person or entity under this license (including the patent license) **terminate automatically.**
|
||||||
|
|
||||||
|
#### 9. Trademark and Name Rights
|
||||||
|
This license grants **no rights** in the "saQut" name, logo, or related trademark elements. In particular:
|
||||||
|
- If you distribute a modified version of the Software, you may not name that version "saQut" or use a name that evokes or is confusingly similar to it.
|
||||||
|
- You may not use the "saQut" name in your products, services, or promotions in a way that implies endorsement, without the prior written permission of the copyright holder.
|
||||||
|
- Honest, descriptive references to the origin of the Software and to the copyright holder (for example, "based on saQut") fall outside the scope of this section.
|
||||||
|
|
||||||
|
#### 10. Attribution Requirement
|
||||||
|
The original copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
#### 11. Termination
|
||||||
|
- Upon your breach of any of the terms of this license, all rights granted to you under it **terminate automatically.**
|
||||||
|
- If your breach is curable and you cure it within **30 (thirty) days** of becoming aware of it — provided the copyright holder has not expressly terminated the license during that period — your rights are reinstated. This cure opportunity does not apply to repeated breaches by the same person.
|
||||||
|
- Termination of your rights does not affect your rights in any Output (Section 4) you previously obtained in compliance with this license.
|
||||||
|
- The disclaimer of warranty and the limitations of liability survive termination.
|
||||||
|
|
||||||
|
#### 12. Disclaimer of Warranty
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Deutsche Lizenzversion
|
||||||
|
|
||||||
|
# Die saQut-Lizenz (Version 1.0)
|
||||||
|
**Quelloffene Lizenz mit eingeschränkter kommerzieller Nutzung**
|
||||||
|
|
||||||
|
Copyright (c) 2026, Abdussamed ULUTAŞ (und saQut-Mitwirkende). Alle Rechte vorbehalten.
|
||||||
|
|
||||||
|
Diese Lizenz gilt für den saQut-Compiler, seinen Quellcode, die Build-Skripte, alle Backend-Komponenten (den Interpreter, die Bytecode-VM, den Compiler und den Transpiler) sowie die dazugehörige Dokumentation und den architektonischen Entwurf (einschließlich der ADR-Dokumente) — zusammenfassend als „die Software" bezeichnet.
|
||||||
|
|
||||||
|
saQut ist ein quelloffenes (einsehbares) Projekt; es ist jedoch **keine freie Software (Free/Libre Software).** Während das Einsehen und Verändern der Software gestattet ist, unterliegt ihre kommerzielle Nutzung den Bedingungen dieser Lizenz und der Genehmigung des Urheberrechtsinhabers.
|
||||||
|
|
||||||
|
#### 1. Die Kernphilosophie (Die Toolbox-Regel)
|
||||||
|
saQut ist als programmierbare, inspizierbare Compiler-Toolbox konzipiert. Ihr Daseinszweck ist es, jede Phase des Kompilierungsprozesses — Tokens, den Abstrakten Syntaxbaum (AST), Symboltabellen und die Zwischendarstellung (IR) — von außen transparent und einsehbar zu halten. Jede Weitergabe oder Veränderung der Software muss diesen Grundsatz der Transparenz wahren.
|
||||||
|
|
||||||
|
#### 2. Definitionen
|
||||||
|
- **Die Software:** Der oben definierte saQut-Compiler und alle seine Komponenten (Quellcode, Interpreter, Bytecode-VM, Compiler- und Transpiler-Backends, Dokumentation und architektonischer Entwurf).
|
||||||
|
- **Ausgabe:** Alles, was mithilfe der Software erzeugt wird; in der saQut-Sprache geschriebener Quellcode (`.sqt`-Dateien), Token-/AST-/Symboltabellen-Ausgaben, erzeugter Bytecode, transpilierter Quellcode, eigenständige ausführbare Dateien sowie alle Ergebnisse, die aus von der Software verarbeiteten Daten gewonnen werden.
|
||||||
|
- **Kommerzielle Nutzung:** Das direkte oder indirekte Erzielen von Einnahmen aus der Software selbst; das Anbieten, Bereitstellen oder Betreiben der Software als Produkt, Dienst, Engine, Komponente oder Automatisierungswerkzeug für oder im Auftrag von Dritten.
|
||||||
|
- **Dritte:** Jeder Kunde, Endnutzer oder sonstige Partei außer der Person oder Organisation, die die Software nutzt.
|
||||||
|
|
||||||
|
#### 3. Genehmigung zum Einsehen, Verändern und zur nicht-kommerziellen Nutzung
|
||||||
|
Diese Lizenz gewährt jeder Person kostenlos die folgenden Rechte:
|
||||||
|
- Die Software zu persönlichen, schulischen/bildenden und nicht-kommerziellen Zwecken zu nutzen, einzusehen und auszuführen.
|
||||||
|
- Den Quellcode der Software einzusehen und zu verändern.
|
||||||
|
- Veränderte oder unveränderte Kopien unter nicht-kommerziellen Bedingungen weiterzugeben, sofern jede weitergegebene Kopie (a) dieser Lizenz unterstellt bleibt, (b) quelloffen (source-available) bleibt und (c) der nachstehenden Namensnennung-Bedingung entspricht.
|
||||||
|
|
||||||
|
#### 4. Die Ausnahme für Ausgabe und Ergebnisse (KRITISCH)
|
||||||
|
Um die Freiheit der Entwickler zu gewährleisten, die die Software nutzen:
|
||||||
|
- **Ihre Ausgabe gehört Ihnen.** Die gesamte in Abschnitt 2 definierte Ausgabe unterliegt **nicht** den Beschränkungen dieser Lizenz. Entwickler behalten das uneingeschränkte Recht, ihre eigene Ausgabe zu nutzen, zu schließen (proprietär zu machen), zu lizenzieren und **kommerziell zu verkaufen.**
|
||||||
|
- **Einnahmen werden aus der Ausgabe erzielt.** Es steht Ihnen frei, die Software als privates Werkzeug auf Ihrem eigenen Rechner (privat oder geschäftlich) zu nutzen und die von Ihnen erzeugten Programme, eigenständigen ausführbaren Dateien oder verarbeiteten Daten (etwa einen Excel-Bericht oder ein Datenbankergebnis) zu verkaufen. In diesem Fall erhält der Dritte nur das Ergebnis, nicht die Software.
|
||||||
|
|
||||||
|
#### 5. Kommerzielle Beschränkungen
|
||||||
|
Da die folgenden Nutzungen darauf hinauslaufen, Einnahmen aus der Software selbst zu erzielen, dürfen sie ohne vorherige schriftliche Genehmigung des Urheberrechtsinhabers nicht vorgenommen werden:
|
||||||
|
- Die Software auf dem Server oder Gerät eines Dritten zu installieren und für ihre Ausführung ein Entgelt zu verlangen (zum Beispiel das Ausführen von `saqut run backup.sql` für einen Kunden gegen Bezahlung).
|
||||||
|
- Die Software als Online-Dienst anzubieten (eine webbasierte IDE oder einen Online-Kompilier- oder -Ausführungsdienst).
|
||||||
|
- Die Software zum Backend oder zur Engine eines kommerziellen Dienstes zu machen, der durch die Live-Anfragen Dritter angetrieben wird.
|
||||||
|
- Die Software als Werkzeug innerhalb einer kommerziellen Automatisierungs-Pipeline zu betreiben oder sie einem System künstlicher Intelligenz als Werkzeug bereitzustellen.
|
||||||
|
- Die Software als Unterkomponente eines kommerziellen Produkts einzubetten (embedden).
|
||||||
|
- Die internen Komponenten oder den architektonischen Entwurf der Software (zum Beispiel ihre AST-Visualisierung oder Optimierungsalgorithmen) als kommerzielles Merkmal oder Produkt wiederzuverwenden.
|
||||||
|
|
||||||
|
**Server- und Versionshinweis:** Solange die Software zur Ausführung die saQut-Laufzeitumgebung (den Interpreter / die Bytecode-VM) benötigt, bedeutet jede kommerzielle serverseitige Nutzung faktisch das Platzieren der Software auf dem Server und unterliegt daher den obigen Beschränkungen. Sobald ein Backend verfügbar ist, das eigenständige ausführbare Dateien erzeugt, die die saQut-Laufzeitumgebung nicht benötigen, ist das kommerzielle Ausführen einer solchen eigenständigen Ausgabe auf einem Server gemäß Abschnitt 4 gestattet.
|
||||||
|
|
||||||
|
#### 6. Genehmigung und kommerzielle Lizenzierung
|
||||||
|
Alle in Abschnitt 5 aufgeführten Beschränkungen stehen ausschließlich dem Urheberrechtsinhaber zu, der allein befugt ist, Ausnahmen zu gewähren. Jede Person oder Organisation, die eine dieser Nutzungen vornehmen möchte, kann beim Urheberrechtsinhaber (Abdussamed ULUTAŞ) eine vorherige schriftliche Genehmigung beantragen; nach deren Erteilung wird die betreffende Nutzung gestattet.
|
||||||
|
Kontakt (Anfragen für kommerzielle Lizenzen): saqutsoftware+gitea@gmail.com
|
||||||
|
|
||||||
|
#### 7. Beiträge (Contributions)
|
||||||
|
Das Beitragen zu saQut ist ein offener Vorschlagsprozess; die endgültige Entscheidung liegt jedoch ausschließlich beim Urheberrechtsinhaber.
|
||||||
|
- Jede Person kann einen Änderungsvorschlag (einen Pull Request / PR) einreichen. Ob ein Beitrag in das Projekt aufgenommen und ob er zusammengeführt (gemerged) wird, entscheidet **allein Abdussamed ULUTAŞ.** Niemand hat ein Recht darauf, dass sein Beitrag in das Projekt aufgenommen wird.
|
||||||
|
- Mit der Einreichung eines Beitrags (Code, Dokumentation, Entwurf oder sonstiges Material) gewährt der Beitragende dem Urheberrechtsinhaber (Abdussamed ULUTAŞ) ein **weltweites, unbefristetes, unwiderrufliches, gebührenfreies und unterlizenzierbares** Recht, diesen Beitrag zu nutzen, zu vervielfältigen, zu verändern, zu verbreiten und **kommerziell zu lizenzieren.** Dieses Recht stellt sicher, dass der Urheberrechtsinhaber die Software einschließlich des Beitrags gemäß Abschnitt 6 kommerziell lizenzieren kann.
|
||||||
|
- Der Beitragende sichert zu, dass das eingereichte Material sein eigenes Werk ist oder dass er über die erforderlichen Rechte verfügt und befugt ist, diese Genehmigung zu erteilen. Die Beitragenden behalten das Urheberrecht an ihren eigenen Beiträgen; die obige Genehmigung wird zusätzlich zu diesen Rechten erteilt.
|
||||||
|
|
||||||
|
#### 8. Patentrechte
|
||||||
|
- **Patentlizenz:** Im Rahmen dieser Lizenz wird jeder Person, die zur Nutzung der Software berechtigt ist, eine gebührenfreie und (vorbehaltlich der nachstehenden Kündigungsbestimmung) unwiderrufliche Patentlizenz an denjenigen Patentansprüchen gewährt, die im Eigentum des Urheberrechtsinhabers und der Beitragenden stehen und die durch die nach dieser Lizenz gestattete Nutzung der Software notwendigerweise verletzt werden.
|
||||||
|
- **Patentvergeltung (automatische Kündigung):** Leitet eine Person oder Organisation einen **Patentrechtsstreit** gegen den Urheberrechtsinhaber oder einen Beitragenden ein und macht dabei geltend, dass die nach dieser Lizenz gestattete Nutzung der Software ein Patent verletzt, so **erlöschen automatisch** alle dieser Person oder Organisation nach dieser Lizenz gewährten Rechte (einschließlich der Patentlizenz).
|
||||||
|
|
||||||
|
#### 9. Marken- und Namensrechte
|
||||||
|
Diese Lizenz gewährt **keinerlei Rechte** an dem Namen „saQut", dem Logo oder verwandten Markenbestandteilen. Insbesondere:
|
||||||
|
- Wenn Sie eine veränderte Version der Software verbreiten, dürfen Sie diese Version nicht „saQut" nennen oder einen Namen verwenden, der daran erinnert oder verwechslungsfähig ähnlich ist.
|
||||||
|
- Sie dürfen den Namen „saQut" ohne vorherige schriftliche Genehmigung des Urheberrechtsinhabers nicht in Ihren Produkten, Diensten oder Werbemaßnahmen in einer Weise verwenden, die eine Billigung suggeriert.
|
||||||
|
- Ehrliche, beschreibende Hinweise auf den Ursprung der Software und auf den Urheberrechtsinhaber (zum Beispiel „basiert auf saQut") fallen nicht unter diesen Abschnitt.
|
||||||
|
|
||||||
|
#### 10. Namensnennung-Bedingung
|
||||||
|
Der ursprüngliche Urheberrechtshinweis und dieser Genehmigungshinweis müssen in allen Kopien oder wesentlichen Teilen der Software enthalten sein.
|
||||||
|
|
||||||
|
#### 11. Kündigung (Termination)
|
||||||
|
- Bei einem Verstoß gegen eine der Bedingungen dieser Lizenz **erlöschen automatisch** alle Ihnen darunter gewährten Rechte.
|
||||||
|
- Ist Ihr Verstoß heilbar und beheben Sie ihn innerhalb von **30 (dreißig) Tagen**, nachdem Sie davon Kenntnis erlangt haben — sofern der Urheberrechtsinhaber die Lizenz in diesem Zeitraum nicht ausdrücklich gekündigt hat —, so werden Ihre Rechte wiederhergestellt. Diese Heilungsmöglichkeit gilt nicht für wiederholte Verstöße derselben Person.
|
||||||
|
- Das Erlöschen Ihrer Rechte berührt nicht Ihre Rechte an einer Ausgabe (Abschnitt 4), die Sie zuvor in Übereinstimmung mit dieser Lizenz erlangt haben.
|
||||||
|
- Der Haftungsausschluss und die Haftungsbeschränkungen gelten auch nach der Kündigung fort.
|
||||||
|
|
||||||
|
#### 12. Haftungsausschluss
|
||||||
|
DIE SOFTWARE WIRD „WIE BESEHEN" (AS IS) OHNE JEGLICHE AUSDRÜCKLICHE ODER STILLSCHWEIGENDE GEWÄHRLEISTUNG ZUR VERFÜGUNG GESTELLT, EINSCHLIESSLICH, ABER NICHT BESCHRÄNKT AUF DIE GEWÄHRLEISTUNG DER MARKTGÄNGIGKEIT, DER EIGNUNG FÜR EINEN BESTIMMTEN ZWECK UND DER NICHTVERLETZUNG VON RECHTEN DRITTER. IN KEINEM FALL SIND DIE AUTOREN ODER URHEBERRECHTSINHABER FÜR ANSPRÜCHE, SCHÄDEN ODER ANDERE HAFTUNGEN HAFTBAR, SEI ES IN FOLGE EINES VERTRAGES, EINER UNERLAUBTEN HANDLUNG ODER AUF ANDERE WEISE, DIE SICH AUS ODER IM ZUSAMMENHANG MIT DER SOFTWARE ODER DER NUTZUNG ODER DEM UMGANG MIT DER SOFTWARE ERGEBEN.
|
||||||
Binary file not shown.
|
|
@ -0,0 +1,23 @@
|
||||||
|
# ninja log v7
|
||||||
|
13 4576 1781796718440262330 CMakeFiles/saqut.dir/src/core/sourcefile.cpp.o 20f68631dd335029
|
||||||
|
13 4805 1781796718441539505 CMakeFiles/saqut.dir/src/lexer/lexer.cpp.o 5fd259c0401f3e22
|
||||||
|
1 2117 1781796771817437208 CMakeFiles/saqut.dir/src/tokenizer/tokenizer.cpp.o fbabe80dcb141239
|
||||||
|
4580 9322 1781796723007277591 CMakeFiles/saqut.dir/src/parser/parser.cpp.o 59cdb5935c541b26
|
||||||
|
21 6876 1781796718448262357 CMakeFiles/saqut.dir/src/parser/nodes/program.cpp.o 9313cba8d8daffed
|
||||||
|
16 6700 1781796718443848488 CMakeFiles/saqut.dir/src/parser/nodes/identifier.cpp.o e3b5b38d75fcd2ca
|
||||||
|
15 7814 1781796718443331391 CMakeFiles/saqut.dir/src/parser/nodes/expressions.cpp.o 5f5bb01381a3c3ad
|
||||||
|
16 7589 1781796718444498682 CMakeFiles/saqut.dir/src/parser/nodes/literal.cpp.o 55743f37408c5f
|
||||||
|
21 7502 1781796718449424977 CMakeFiles/saqut.dir/src/parser/nodes/statements.cpp.o 3c8869307381c930
|
||||||
|
14 6864 1781796718442362341 CMakeFiles/saqut.dir/src/parser/nodes/binary_expr.cpp.o 5cc8b697133bcf64
|
||||||
|
15 6733 1781796718442847556 CMakeFiles/saqut.dir/src/parser/nodes/declarations.cpp.o c3d262615ede4c95
|
||||||
|
1 4629 1781801148234045650 CMakeFiles/saqut.dir/src/main.cpp.o 3cfef7a665d5bf87
|
||||||
|
4629 4900 1781801152862380672 saqut f2e198803c4dbffb
|
||||||
|
0 22 1781801180493522122 build.ninja 1876a59d627a585
|
||||||
|
0 22 1781801180493522122 /home/saqut/Masaüstü/saqutcompiler/build/cmake_install.cmake 1876a59d627a585
|
||||||
|
6733 11112 1781796725160284765 CMakeFiles/saqut.dir/src/symbol/symbol_collector.cpp.o ec4e483b8ddb4007
|
||||||
|
4805 9685 1781796723232278341 CMakeFiles/saqut.dir/src/semantic/structural_validator.cpp.o 248faa3675024351
|
||||||
|
6700 10405 1781796725127284655 CMakeFiles/saqut.dir/src/semantic/type_checker.cpp.o b29c133293d988b0
|
||||||
|
1 850 1781801148235507662 CMakeFiles/saqut.dir/src/vm/interpreter.cpp.o b7dd80e002d68a1d
|
||||||
|
2 957 1781800866770475511 CMakeFiles/saqut.dir/src/ir/ir_function.cpp.o 10f5e8dfd1461d69
|
||||||
|
2 718 1781800866771246136 CMakeFiles/saqut.dir/src/ir/ir_program.cpp.o 9518231d970828da
|
||||||
|
2 3169 1781800866771136888 CMakeFiles/saqut.dir/src/ir/ir_generator.cpp.o 10a1ed4e1f52e530
|
||||||
|
|
@ -0,0 +1,363 @@
|
||||||
|
# This is the CMakeCache file.
|
||||||
|
# For build in directory: /home/saqut/Masaüstü/saqutcompiler/build
|
||||||
|
# It was generated by CMake: /usr/bin/cmake
|
||||||
|
# You can edit this file to change values found and used by cmake.
|
||||||
|
# If you do not want to change any of the values, simply exit the editor.
|
||||||
|
# If you do want to change a value, simply edit, save, and exit the editor.
|
||||||
|
# The syntax for the file is as follows:
|
||||||
|
# KEY:TYPE=VALUE
|
||||||
|
# KEY is the name of a variable in the cache.
|
||||||
|
# TYPE is a hint to GUIs for the type of VALUE, DO NOT EDIT TYPE!.
|
||||||
|
# VALUE is the current value for the KEY.
|
||||||
|
|
||||||
|
########################
|
||||||
|
# EXTERNAL cache entries
|
||||||
|
########################
|
||||||
|
|
||||||
|
//Path to a program.
|
||||||
|
CMAKE_ADDR2LINE:FILEPATH=/usr/bin/addr2line
|
||||||
|
|
||||||
|
//Path to a program.
|
||||||
|
CMAKE_AR:FILEPATH=/usr/bin/ar
|
||||||
|
|
||||||
|
//Choose the type of build, options are: None Debug Release RelWithDebInfo
|
||||||
|
// MinSizeRel ...
|
||||||
|
CMAKE_BUILD_TYPE:STRING=Debug
|
||||||
|
|
||||||
|
//No help, variable specified on the command line.
|
||||||
|
CMAKE_CXX_COMPILER:FILEPATH=/usr/bin/g++
|
||||||
|
|
||||||
|
//A wrapper around 'ar' adding the appropriate '--plugin' option
|
||||||
|
// for the GCC compiler
|
||||||
|
CMAKE_CXX_COMPILER_AR:FILEPATH=/usr/bin/gcc-ar
|
||||||
|
|
||||||
|
//A wrapper around 'ranlib' adding the appropriate '--plugin' option
|
||||||
|
// for the GCC compiler
|
||||||
|
CMAKE_CXX_COMPILER_RANLIB:FILEPATH=/usr/bin/gcc-ranlib
|
||||||
|
|
||||||
|
//Flags used by the CXX compiler during all build types.
|
||||||
|
CMAKE_CXX_FLAGS:STRING=
|
||||||
|
|
||||||
|
//Flags used by the CXX compiler during DEBUG builds.
|
||||||
|
CMAKE_CXX_FLAGS_DEBUG:STRING=-g
|
||||||
|
|
||||||
|
//Flags used by the CXX compiler during MINSIZEREL builds.
|
||||||
|
CMAKE_CXX_FLAGS_MINSIZEREL:STRING=-Os -DNDEBUG
|
||||||
|
|
||||||
|
//Flags used by the CXX compiler during RELEASE builds.
|
||||||
|
CMAKE_CXX_FLAGS_RELEASE:STRING=-O3 -DNDEBUG
|
||||||
|
|
||||||
|
//Flags used by the CXX compiler during RELWITHDEBINFO builds.
|
||||||
|
CMAKE_CXX_FLAGS_RELWITHDEBINFO:STRING=-O2 -g -DNDEBUG
|
||||||
|
|
||||||
|
//No help, variable specified on the command line.
|
||||||
|
CMAKE_C_COMPILER:FILEPATH=/usr/bin/gcc
|
||||||
|
|
||||||
|
//Path to a program.
|
||||||
|
CMAKE_DLLTOOL:FILEPATH=CMAKE_DLLTOOL-NOTFOUND
|
||||||
|
|
||||||
|
//Flags used by the linker during all build types.
|
||||||
|
CMAKE_EXE_LINKER_FLAGS:STRING=
|
||||||
|
|
||||||
|
//Flags used by the linker during DEBUG builds.
|
||||||
|
CMAKE_EXE_LINKER_FLAGS_DEBUG:STRING=
|
||||||
|
|
||||||
|
//Flags used by the linker during MINSIZEREL builds.
|
||||||
|
CMAKE_EXE_LINKER_FLAGS_MINSIZEREL:STRING=
|
||||||
|
|
||||||
|
//Flags used by the linker during RELEASE builds.
|
||||||
|
CMAKE_EXE_LINKER_FLAGS_RELEASE:STRING=
|
||||||
|
|
||||||
|
//Flags used by the linker during RELWITHDEBINFO builds.
|
||||||
|
CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO:STRING=
|
||||||
|
|
||||||
|
//Enable/Disable output of build database during the build.
|
||||||
|
CMAKE_EXPORT_BUILD_DATABASE:BOOL=
|
||||||
|
|
||||||
|
//No help, variable specified on the command line.
|
||||||
|
CMAKE_EXPORT_COMPILE_COMMANDS:BOOL=TRUE
|
||||||
|
|
||||||
|
//Value Computed by CMake.
|
||||||
|
CMAKE_FIND_PACKAGE_REDIRECTS_DIR:STATIC=/home/saqut/Masaüstü/saqutcompiler/build/CMakeFiles/pkgRedirects
|
||||||
|
|
||||||
|
//Install path prefix, prepended onto install directories.
|
||||||
|
CMAKE_INSTALL_PREFIX:PATH=/usr/local
|
||||||
|
|
||||||
|
//Path to a program.
|
||||||
|
CMAKE_LINKER:FILEPATH=/usr/bin/ld
|
||||||
|
|
||||||
|
//Program used to build from build.ninja files.
|
||||||
|
CMAKE_MAKE_PROGRAM:FILEPATH=/usr/bin/ninja
|
||||||
|
|
||||||
|
//Flags used by the linker during the creation of modules during
|
||||||
|
// all build types.
|
||||||
|
CMAKE_MODULE_LINKER_FLAGS:STRING=
|
||||||
|
|
||||||
|
//Flags used by the linker during the creation of modules during
|
||||||
|
// DEBUG builds.
|
||||||
|
CMAKE_MODULE_LINKER_FLAGS_DEBUG:STRING=
|
||||||
|
|
||||||
|
//Flags used by the linker during the creation of modules during
|
||||||
|
// MINSIZEREL builds.
|
||||||
|
CMAKE_MODULE_LINKER_FLAGS_MINSIZEREL:STRING=
|
||||||
|
|
||||||
|
//Flags used by the linker during the creation of modules during
|
||||||
|
// RELEASE builds.
|
||||||
|
CMAKE_MODULE_LINKER_FLAGS_RELEASE:STRING=
|
||||||
|
|
||||||
|
//Flags used by the linker during the creation of modules during
|
||||||
|
// RELWITHDEBINFO builds.
|
||||||
|
CMAKE_MODULE_LINKER_FLAGS_RELWITHDEBINFO:STRING=
|
||||||
|
|
||||||
|
//Path to a program.
|
||||||
|
CMAKE_NM:FILEPATH=/usr/bin/nm
|
||||||
|
|
||||||
|
//Path to a program.
|
||||||
|
CMAKE_OBJCOPY:FILEPATH=/usr/bin/objcopy
|
||||||
|
|
||||||
|
//Path to a program.
|
||||||
|
CMAKE_OBJDUMP:FILEPATH=/usr/bin/objdump
|
||||||
|
|
||||||
|
//Value Computed by CMake
|
||||||
|
CMAKE_PROJECT_COMPAT_VERSION:STATIC=
|
||||||
|
|
||||||
|
//Value Computed by CMake
|
||||||
|
CMAKE_PROJECT_DESCRIPTION:STATIC=
|
||||||
|
|
||||||
|
//Value Computed by CMake
|
||||||
|
CMAKE_PROJECT_HOMEPAGE_URL:STATIC=
|
||||||
|
|
||||||
|
//Value Computed by CMake
|
||||||
|
CMAKE_PROJECT_NAME:STATIC=saqut
|
||||||
|
|
||||||
|
//Value Computed by CMake
|
||||||
|
CMAKE_PROJECT_SPDX_LICENSE:STATIC=
|
||||||
|
|
||||||
|
//Value Computed by CMake
|
||||||
|
CMAKE_PROJECT_VERSION:STATIC=0.1
|
||||||
|
|
||||||
|
//Value Computed by CMake
|
||||||
|
CMAKE_PROJECT_VERSION_MAJOR:STATIC=0
|
||||||
|
|
||||||
|
//Value Computed by CMake
|
||||||
|
CMAKE_PROJECT_VERSION_MINOR:STATIC=1
|
||||||
|
|
||||||
|
//Value Computed by CMake
|
||||||
|
CMAKE_PROJECT_VERSION_PATCH:STATIC=
|
||||||
|
|
||||||
|
//Value Computed by CMake
|
||||||
|
CMAKE_PROJECT_VERSION_TWEAK:STATIC=
|
||||||
|
|
||||||
|
//Path to a program.
|
||||||
|
CMAKE_RANLIB:FILEPATH=/usr/bin/ranlib
|
||||||
|
|
||||||
|
//Path to a program.
|
||||||
|
CMAKE_READELF:FILEPATH=/usr/bin/readelf
|
||||||
|
|
||||||
|
//Flags used by the linker during the creation of shared libraries
|
||||||
|
// during all build types.
|
||||||
|
CMAKE_SHARED_LINKER_FLAGS:STRING=
|
||||||
|
|
||||||
|
//Flags used by the linker during the creation of shared libraries
|
||||||
|
// during DEBUG builds.
|
||||||
|
CMAKE_SHARED_LINKER_FLAGS_DEBUG:STRING=
|
||||||
|
|
||||||
|
//Flags used by the linker during the creation of shared libraries
|
||||||
|
// during MINSIZEREL builds.
|
||||||
|
CMAKE_SHARED_LINKER_FLAGS_MINSIZEREL:STRING=
|
||||||
|
|
||||||
|
//Flags used by the linker during the creation of shared libraries
|
||||||
|
// during RELEASE builds.
|
||||||
|
CMAKE_SHARED_LINKER_FLAGS_RELEASE:STRING=
|
||||||
|
|
||||||
|
//Flags used by the linker during the creation of shared libraries
|
||||||
|
// during RELWITHDEBINFO builds.
|
||||||
|
CMAKE_SHARED_LINKER_FLAGS_RELWITHDEBINFO:STRING=
|
||||||
|
|
||||||
|
//If set, runtime paths are not added when installing shared libraries,
|
||||||
|
// but are added when building.
|
||||||
|
CMAKE_SKIP_INSTALL_RPATH:BOOL=NO
|
||||||
|
|
||||||
|
//If set, runtime paths are not added when using shared libraries.
|
||||||
|
CMAKE_SKIP_RPATH:BOOL=NO
|
||||||
|
|
||||||
|
//Flags used by the archiver during the creation of static libraries
|
||||||
|
// during all build types.
|
||||||
|
CMAKE_STATIC_LINKER_FLAGS:STRING=
|
||||||
|
|
||||||
|
//Flags used by the archiver during the creation of static libraries
|
||||||
|
// during DEBUG builds.
|
||||||
|
CMAKE_STATIC_LINKER_FLAGS_DEBUG:STRING=
|
||||||
|
|
||||||
|
//Flags used by the archiver during the creation of static libraries
|
||||||
|
// during MINSIZEREL builds.
|
||||||
|
CMAKE_STATIC_LINKER_FLAGS_MINSIZEREL:STRING=
|
||||||
|
|
||||||
|
//Flags used by the archiver during the creation of static libraries
|
||||||
|
// during RELEASE builds.
|
||||||
|
CMAKE_STATIC_LINKER_FLAGS_RELEASE:STRING=
|
||||||
|
|
||||||
|
//Flags used by the archiver during the creation of static libraries
|
||||||
|
// during RELWITHDEBINFO builds.
|
||||||
|
CMAKE_STATIC_LINKER_FLAGS_RELWITHDEBINFO:STRING=
|
||||||
|
|
||||||
|
//Path to a program.
|
||||||
|
CMAKE_STRIP:FILEPATH=/usr/bin/strip
|
||||||
|
|
||||||
|
//Path to a program.
|
||||||
|
CMAKE_TAPI:FILEPATH=CMAKE_TAPI-NOTFOUND
|
||||||
|
|
||||||
|
//If this value is on, makefiles will be generated without the
|
||||||
|
// .SILENT directive, and all commands will be echoed to the console
|
||||||
|
// during the make. This is useful for debugging only. With Visual
|
||||||
|
// Studio IDE projects all commands are done without /nologo.
|
||||||
|
CMAKE_VERBOSE_MAKEFILE:BOOL=FALSE
|
||||||
|
|
||||||
|
//Value Computed by CMake
|
||||||
|
saqut_BINARY_DIR:STATIC=/home/saqut/Masaüstü/saqutcompiler/build
|
||||||
|
|
||||||
|
//Value Computed by CMake
|
||||||
|
saqut_IS_TOP_LEVEL:STATIC=ON
|
||||||
|
|
||||||
|
//Value Computed by CMake
|
||||||
|
saqut_SOURCE_DIR:STATIC=/home/saqut/Masaüstü/saqutcompiler
|
||||||
|
|
||||||
|
|
||||||
|
########################
|
||||||
|
# INTERNAL cache entries
|
||||||
|
########################
|
||||||
|
|
||||||
|
//ADVANCED property for variable: CMAKE_ADDR2LINE
|
||||||
|
CMAKE_ADDR2LINE-ADVANCED:INTERNAL=1
|
||||||
|
//ADVANCED property for variable: CMAKE_AR
|
||||||
|
CMAKE_AR-ADVANCED:INTERNAL=1
|
||||||
|
//This is the directory where this CMakeCache.txt was created
|
||||||
|
CMAKE_CACHEFILE_DIR:INTERNAL=/home/saqut/Masaüstü/saqutcompiler/build
|
||||||
|
//Major version of cmake used to create the current loaded cache
|
||||||
|
CMAKE_CACHE_MAJOR_VERSION:INTERNAL=4
|
||||||
|
//Minor version of cmake used to create the current loaded cache
|
||||||
|
CMAKE_CACHE_MINOR_VERSION:INTERNAL=3
|
||||||
|
//Patch version of cmake used to create the current loaded cache
|
||||||
|
CMAKE_CACHE_PATCH_VERSION:INTERNAL=3
|
||||||
|
//Path to CMake executable.
|
||||||
|
CMAKE_COMMAND:INTERNAL=/usr/bin/cmake
|
||||||
|
//Path to cpack program executable.
|
||||||
|
CMAKE_CPACK_COMMAND:INTERNAL=/usr/bin/cpack
|
||||||
|
//Path to ctest program executable.
|
||||||
|
CMAKE_CTEST_COMMAND:INTERNAL=/usr/bin/ctest
|
||||||
|
//ADVANCED property for variable: CMAKE_CXX_COMPILER
|
||||||
|
CMAKE_CXX_COMPILER-ADVANCED:INTERNAL=1
|
||||||
|
//ADVANCED property for variable: CMAKE_CXX_COMPILER_AR
|
||||||
|
CMAKE_CXX_COMPILER_AR-ADVANCED:INTERNAL=1
|
||||||
|
//ADVANCED property for variable: CMAKE_CXX_COMPILER_RANLIB
|
||||||
|
CMAKE_CXX_COMPILER_RANLIB-ADVANCED:INTERNAL=1
|
||||||
|
//ADVANCED property for variable: CMAKE_CXX_FLAGS
|
||||||
|
CMAKE_CXX_FLAGS-ADVANCED:INTERNAL=1
|
||||||
|
//ADVANCED property for variable: CMAKE_CXX_FLAGS_DEBUG
|
||||||
|
CMAKE_CXX_FLAGS_DEBUG-ADVANCED:INTERNAL=1
|
||||||
|
//ADVANCED property for variable: CMAKE_CXX_FLAGS_MINSIZEREL
|
||||||
|
CMAKE_CXX_FLAGS_MINSIZEREL-ADVANCED:INTERNAL=1
|
||||||
|
//ADVANCED property for variable: CMAKE_CXX_FLAGS_RELEASE
|
||||||
|
CMAKE_CXX_FLAGS_RELEASE-ADVANCED:INTERNAL=1
|
||||||
|
//ADVANCED property for variable: CMAKE_CXX_FLAGS_RELWITHDEBINFO
|
||||||
|
CMAKE_CXX_FLAGS_RELWITHDEBINFO-ADVANCED:INTERNAL=1
|
||||||
|
//ADVANCED property for variable: CMAKE_DLLTOOL
|
||||||
|
CMAKE_DLLTOOL-ADVANCED:INTERNAL=1
|
||||||
|
//Path to cache edit program executable.
|
||||||
|
CMAKE_EDIT_COMMAND:INTERNAL=/usr/bin/ccmake
|
||||||
|
//Executable file format
|
||||||
|
CMAKE_EXECUTABLE_FORMAT:INTERNAL=ELF
|
||||||
|
//ADVANCED property for variable: CMAKE_EXE_LINKER_FLAGS
|
||||||
|
CMAKE_EXE_LINKER_FLAGS-ADVANCED:INTERNAL=1
|
||||||
|
//ADVANCED property for variable: CMAKE_EXE_LINKER_FLAGS_DEBUG
|
||||||
|
CMAKE_EXE_LINKER_FLAGS_DEBUG-ADVANCED:INTERNAL=1
|
||||||
|
//ADVANCED property for variable: CMAKE_EXE_LINKER_FLAGS_MINSIZEREL
|
||||||
|
CMAKE_EXE_LINKER_FLAGS_MINSIZEREL-ADVANCED:INTERNAL=1
|
||||||
|
//ADVANCED property for variable: CMAKE_EXE_LINKER_FLAGS_RELEASE
|
||||||
|
CMAKE_EXE_LINKER_FLAGS_RELEASE-ADVANCED:INTERNAL=1
|
||||||
|
//ADVANCED property for variable: CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO
|
||||||
|
CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO-ADVANCED:INTERNAL=1
|
||||||
|
//ADVANCED property for variable: CMAKE_EXPORT_BUILD_DATABASE
|
||||||
|
CMAKE_EXPORT_BUILD_DATABASE-ADVANCED:INTERNAL=1
|
||||||
|
//ADVANCED property for variable: CMAKE_EXPORT_COMPILE_COMMANDS
|
||||||
|
CMAKE_EXPORT_COMPILE_COMMANDS-ADVANCED:INTERNAL=1
|
||||||
|
//Name of external makefile project generator.
|
||||||
|
CMAKE_EXTRA_GENERATOR:INTERNAL=
|
||||||
|
//Name of generator.
|
||||||
|
CMAKE_GENERATOR:INTERNAL=Ninja
|
||||||
|
//Generator instance identifier.
|
||||||
|
CMAKE_GENERATOR_INSTANCE:INTERNAL=
|
||||||
|
//Name of generator platform.
|
||||||
|
CMAKE_GENERATOR_PLATFORM:INTERNAL=
|
||||||
|
//Name of generator toolset.
|
||||||
|
CMAKE_GENERATOR_TOOLSET:INTERNAL=
|
||||||
|
//Source directory with the top level CMakeLists.txt file for this
|
||||||
|
// project
|
||||||
|
CMAKE_HOME_DIRECTORY:INTERNAL=/home/saqut/Masaüstü/saqutcompiler
|
||||||
|
//Install .so files without execute permission.
|
||||||
|
CMAKE_INSTALL_SO_NO_EXE:INTERNAL=0
|
||||||
|
//ADVANCED property for variable: CMAKE_LINKER
|
||||||
|
CMAKE_LINKER-ADVANCED:INTERNAL=1
|
||||||
|
//Name of CMakeLists files to read
|
||||||
|
CMAKE_LIST_FILE_NAME:INTERNAL=CMakeLists.txt
|
||||||
|
//ADVANCED property for variable: CMAKE_MAKE_PROGRAM
|
||||||
|
CMAKE_MAKE_PROGRAM-ADVANCED:INTERNAL=1
|
||||||
|
//ADVANCED property for variable: CMAKE_MODULE_LINKER_FLAGS
|
||||||
|
CMAKE_MODULE_LINKER_FLAGS-ADVANCED:INTERNAL=1
|
||||||
|
//ADVANCED property for variable: CMAKE_MODULE_LINKER_FLAGS_DEBUG
|
||||||
|
CMAKE_MODULE_LINKER_FLAGS_DEBUG-ADVANCED:INTERNAL=1
|
||||||
|
//ADVANCED property for variable: CMAKE_MODULE_LINKER_FLAGS_MINSIZEREL
|
||||||
|
CMAKE_MODULE_LINKER_FLAGS_MINSIZEREL-ADVANCED:INTERNAL=1
|
||||||
|
//ADVANCED property for variable: CMAKE_MODULE_LINKER_FLAGS_RELEASE
|
||||||
|
CMAKE_MODULE_LINKER_FLAGS_RELEASE-ADVANCED:INTERNAL=1
|
||||||
|
//ADVANCED property for variable: CMAKE_MODULE_LINKER_FLAGS_RELWITHDEBINFO
|
||||||
|
CMAKE_MODULE_LINKER_FLAGS_RELWITHDEBINFO-ADVANCED:INTERNAL=1
|
||||||
|
//ADVANCED property for variable: CMAKE_NM
|
||||||
|
CMAKE_NM-ADVANCED:INTERNAL=1
|
||||||
|
//number of local generators
|
||||||
|
CMAKE_NUMBER_OF_MAKEFILES:INTERNAL=1
|
||||||
|
//ADVANCED property for variable: CMAKE_OBJCOPY
|
||||||
|
CMAKE_OBJCOPY-ADVANCED:INTERNAL=1
|
||||||
|
//ADVANCED property for variable: CMAKE_OBJDUMP
|
||||||
|
CMAKE_OBJDUMP-ADVANCED:INTERNAL=1
|
||||||
|
//Platform information initialized
|
||||||
|
CMAKE_PLATFORM_INFO_INITIALIZED:INTERNAL=1
|
||||||
|
//ADVANCED property for variable: CMAKE_RANLIB
|
||||||
|
CMAKE_RANLIB-ADVANCED:INTERNAL=1
|
||||||
|
//ADVANCED property for variable: CMAKE_READELF
|
||||||
|
CMAKE_READELF-ADVANCED:INTERNAL=1
|
||||||
|
//Path to CMake installation.
|
||||||
|
CMAKE_ROOT:INTERNAL=/usr/share/cmake
|
||||||
|
//ADVANCED property for variable: CMAKE_SHARED_LINKER_FLAGS
|
||||||
|
CMAKE_SHARED_LINKER_FLAGS-ADVANCED:INTERNAL=1
|
||||||
|
//ADVANCED property for variable: CMAKE_SHARED_LINKER_FLAGS_DEBUG
|
||||||
|
CMAKE_SHARED_LINKER_FLAGS_DEBUG-ADVANCED:INTERNAL=1
|
||||||
|
//ADVANCED property for variable: CMAKE_SHARED_LINKER_FLAGS_MINSIZEREL
|
||||||
|
CMAKE_SHARED_LINKER_FLAGS_MINSIZEREL-ADVANCED:INTERNAL=1
|
||||||
|
//ADVANCED property for variable: CMAKE_SHARED_LINKER_FLAGS_RELEASE
|
||||||
|
CMAKE_SHARED_LINKER_FLAGS_RELEASE-ADVANCED:INTERNAL=1
|
||||||
|
//ADVANCED property for variable: CMAKE_SHARED_LINKER_FLAGS_RELWITHDEBINFO
|
||||||
|
CMAKE_SHARED_LINKER_FLAGS_RELWITHDEBINFO-ADVANCED:INTERNAL=1
|
||||||
|
//ADVANCED property for variable: CMAKE_SKIP_INSTALL_RPATH
|
||||||
|
CMAKE_SKIP_INSTALL_RPATH-ADVANCED:INTERNAL=1
|
||||||
|
//ADVANCED property for variable: CMAKE_SKIP_RPATH
|
||||||
|
CMAKE_SKIP_RPATH-ADVANCED:INTERNAL=1
|
||||||
|
//ADVANCED property for variable: CMAKE_STATIC_LINKER_FLAGS
|
||||||
|
CMAKE_STATIC_LINKER_FLAGS-ADVANCED:INTERNAL=1
|
||||||
|
//ADVANCED property for variable: CMAKE_STATIC_LINKER_FLAGS_DEBUG
|
||||||
|
CMAKE_STATIC_LINKER_FLAGS_DEBUG-ADVANCED:INTERNAL=1
|
||||||
|
//ADVANCED property for variable: CMAKE_STATIC_LINKER_FLAGS_MINSIZEREL
|
||||||
|
CMAKE_STATIC_LINKER_FLAGS_MINSIZEREL-ADVANCED:INTERNAL=1
|
||||||
|
//ADVANCED property for variable: CMAKE_STATIC_LINKER_FLAGS_RELEASE
|
||||||
|
CMAKE_STATIC_LINKER_FLAGS_RELEASE-ADVANCED:INTERNAL=1
|
||||||
|
//ADVANCED property for variable: CMAKE_STATIC_LINKER_FLAGS_RELWITHDEBINFO
|
||||||
|
CMAKE_STATIC_LINKER_FLAGS_RELWITHDEBINFO-ADVANCED:INTERNAL=1
|
||||||
|
//ADVANCED property for variable: CMAKE_STRIP
|
||||||
|
CMAKE_STRIP-ADVANCED:INTERNAL=1
|
||||||
|
//ADVANCED property for variable: CMAKE_TAPI
|
||||||
|
CMAKE_TAPI-ADVANCED:INTERNAL=1
|
||||||
|
//uname command
|
||||||
|
CMAKE_UNAME:INTERNAL=/usr/bin/uname
|
||||||
|
//ADVANCED property for variable: CMAKE_VERBOSE_MAKEFILE
|
||||||
|
CMAKE_VERBOSE_MAKEFILE-ADVANCED:INTERNAL=1
|
||||||
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"InstallScripts" :
|
||||||
|
[
|
||||||
|
"/home/saqut/Masa\u00fcst\u00fc/saqutcompiler/build/cmake_install.cmake"
|
||||||
|
],
|
||||||
|
"Parallel" : false
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
/home/saqut/Masaüstü/saqutcompiler/build/CMakeFiles/saqut.dir
|
||||||
|
/home/saqut/Masaüstü/saqutcompiler/build/CMakeFiles/edit_cache.dir
|
||||||
|
/home/saqut/Masaüstü/saqutcompiler/build/CMakeFiles/rebuild_cache.dir
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
# This file is generated by cmake for dependency checking of the CMakeCache.txt file
|
||||||
|
|
@ -0,0 +1,96 @@
|
||||||
|
# CMAKE generated file: DO NOT EDIT!
|
||||||
|
# Generated by "Ninja" Generator, CMake Version 4.3
|
||||||
|
|
||||||
|
# This file contains all the rules used to get the outputs files
|
||||||
|
# built from the input files.
|
||||||
|
# It is included in the main 'build.ninja'.
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# Project: saqut
|
||||||
|
# Configurations: Debug
|
||||||
|
# =============================================================================
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
#############################################
|
||||||
|
# Rule for generating CXX dependencies.
|
||||||
|
|
||||||
|
rule CXX_SCAN__saqut_Debug
|
||||||
|
depfile = $DEP_FILE
|
||||||
|
command = /usr/bin/g++ $DEFINES $INCLUDES $FLAGS -E -x c++ $in -MT $DYNDEP_INTERMEDIATE_FILE -MD -MF $DEP_FILE -fmodules-ts -fdeps-file=$DYNDEP_INTERMEDIATE_FILE -fdeps-target=$OBJ_FILE -fdeps-format=p1689r5 -o $PREPROCESSED_OUTPUT_FILE
|
||||||
|
description = Scanning $in for CXX dependencies
|
||||||
|
|
||||||
|
|
||||||
|
#############################################
|
||||||
|
# Rule to generate ninja dyndep files for CXX.
|
||||||
|
|
||||||
|
rule CXX_DYNDEP__saqut_Debug
|
||||||
|
command = /usr/bin/cmake -E cmake_ninja_dyndep --tdi=CMakeFiles/saqut.dir/CXXDependInfo.json --lang=CXX --modmapfmt=gcc --dd=$out @$out.rsp
|
||||||
|
description = Generating CXX dyndep file $out
|
||||||
|
rspfile = $out.rsp
|
||||||
|
rspfile_content = $in
|
||||||
|
restat = 1
|
||||||
|
|
||||||
|
|
||||||
|
#############################################
|
||||||
|
# Rule for compiling CXX files.
|
||||||
|
|
||||||
|
rule CXX_COMPILER__saqut_scanned_Debug
|
||||||
|
depfile = $DEP_FILE
|
||||||
|
deps = gcc
|
||||||
|
command = ${LAUNCHER}${CODE_CHECK}/usr/bin/g++ $DEFINES $INCLUDES $FLAGS -MD -MT $out -MF $DEP_FILE -fmodules-ts -fmodule-mapper=$DYNDEP_MODULE_MAP_FILE -MD -fdeps-format=p1689r5 -x c++ -o $out -c $in
|
||||||
|
description = Building CXX object $out
|
||||||
|
|
||||||
|
|
||||||
|
#############################################
|
||||||
|
# Rule for compiling CXX files.
|
||||||
|
|
||||||
|
rule CXX_COMPILER__saqut_unscanned_Debug
|
||||||
|
depfile = $DEP_FILE
|
||||||
|
deps = gcc
|
||||||
|
command = ${LAUNCHER}${CODE_CHECK}/usr/bin/g++ $DEFINES $INCLUDES $FLAGS -MD -MT $out -MF $DEP_FILE -o $out -c $in
|
||||||
|
description = Building CXX object $out
|
||||||
|
|
||||||
|
|
||||||
|
#############################################
|
||||||
|
# Rule for linking CXX executable.
|
||||||
|
|
||||||
|
rule CXX_EXECUTABLE_LINKER__saqut_Debug
|
||||||
|
depfile = $DEP_FILE
|
||||||
|
deps = gcc
|
||||||
|
command = $PRE_LINK && /usr/bin/g++ $FLAGS $LINK_FLAGS $in -o $TARGET_FILE $LINK_PATH $LINK_LIBRARIES && $POST_BUILD
|
||||||
|
description = Linking CXX executable $TARGET_FILE
|
||||||
|
restat = $RESTAT
|
||||||
|
|
||||||
|
|
||||||
|
#############################################
|
||||||
|
# Rule for running custom commands.
|
||||||
|
|
||||||
|
rule CUSTOM_COMMAND
|
||||||
|
command = $COMMAND
|
||||||
|
description = $DESC
|
||||||
|
|
||||||
|
|
||||||
|
#############################################
|
||||||
|
# Rule for re-running cmake.
|
||||||
|
|
||||||
|
rule RERUN_CMAKE
|
||||||
|
command = /usr/bin/cmake --regenerate-during-build -S/home/saqut/Masaüstü/saqutcompiler -B/home/saqut/Masaüstü/saqutcompiler/build
|
||||||
|
description = Re-running CMake...
|
||||||
|
generator = 1
|
||||||
|
|
||||||
|
|
||||||
|
#############################################
|
||||||
|
# Rule for cleaning all built files.
|
||||||
|
|
||||||
|
rule CLEAN
|
||||||
|
command = /usr/bin/ninja $FILE_ARG -t clean $TARGETS
|
||||||
|
description = Cleaning all built files...
|
||||||
|
|
||||||
|
|
||||||
|
#############################################
|
||||||
|
# Rule for printing all primary targets available.
|
||||||
|
|
||||||
|
rule HELP
|
||||||
|
command = /usr/bin/ninja -t targets
|
||||||
|
description = All primary targets available:
|
||||||
|
|
||||||
|
|
@ -0,0 +1,312 @@
|
||||||
|
# CMAKE generated file: DO NOT EDIT!
|
||||||
|
# Generated by "Ninja" Generator, CMake Version 4.3
|
||||||
|
|
||||||
|
# This file contains all the build statements describing the
|
||||||
|
# compilation DAG.
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# Write statements declared in CMakeLists.txt:
|
||||||
|
#
|
||||||
|
# Which is the root file.
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# Project: saqut
|
||||||
|
# Configurations: Debug
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
#############################################
|
||||||
|
# Minimal version of Ninja required by this file
|
||||||
|
|
||||||
|
ninja_required_version = 1.5
|
||||||
|
|
||||||
|
|
||||||
|
#############################################
|
||||||
|
# Set configuration variable for custom commands.
|
||||||
|
|
||||||
|
CONFIGURATION = Debug
|
||||||
|
# =============================================================================
|
||||||
|
# Include auxiliary files.
|
||||||
|
|
||||||
|
|
||||||
|
#############################################
|
||||||
|
# Include rules file.
|
||||||
|
|
||||||
|
include CMakeFiles/rules.ninja
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
#############################################
|
||||||
|
# Logical path to working directory; prefix for absolute paths.
|
||||||
|
|
||||||
|
cmake_ninja_workdir = /home/saqut/Masaüstü/saqutcompiler/build/
|
||||||
|
# =============================================================================
|
||||||
|
# Object build statements for EXECUTABLE target saqut
|
||||||
|
|
||||||
|
|
||||||
|
#############################################
|
||||||
|
# Order-only phony target for saqut
|
||||||
|
|
||||||
|
build cmake_object_order_depends_target_saqut: phony || .
|
||||||
|
|
||||||
|
build CMakeFiles/saqut.dir/src/core/sourcefile.cpp.o: CXX_COMPILER__saqut_unscanned_Debug /home/saqut/Masaüstü/saqutcompiler/src/core/sourcefile.cpp || cmake_object_order_depends_target_saqut
|
||||||
|
CONFIG = Debug
|
||||||
|
DEP_FILE = CMakeFiles/saqut.dir/src/core/sourcefile.cpp.o.d
|
||||||
|
FLAGS = -g -std=gnu++20 -Wall -Wextra -g -O0
|
||||||
|
INCLUDES = -I/home/saqut/Masaüstü/saqutcompiler/src
|
||||||
|
OBJECT_DIR = CMakeFiles/saqut.dir
|
||||||
|
OBJECT_FILE_DIR = CMakeFiles/saqut.dir/src/core
|
||||||
|
TARGET_SUPPORT_DIR = CMakeFiles/saqut.dir
|
||||||
|
|
||||||
|
build CMakeFiles/saqut.dir/src/ir/ir_function.cpp.o: CXX_COMPILER__saqut_unscanned_Debug /home/saqut/Masaüstü/saqutcompiler/src/ir/ir_function.cpp || cmake_object_order_depends_target_saqut
|
||||||
|
CONFIG = Debug
|
||||||
|
DEP_FILE = CMakeFiles/saqut.dir/src/ir/ir_function.cpp.o.d
|
||||||
|
FLAGS = -g -std=gnu++20 -Wall -Wextra -g -O0
|
||||||
|
INCLUDES = -I/home/saqut/Masaüstü/saqutcompiler/src
|
||||||
|
OBJECT_DIR = CMakeFiles/saqut.dir
|
||||||
|
OBJECT_FILE_DIR = CMakeFiles/saqut.dir/src/ir
|
||||||
|
TARGET_SUPPORT_DIR = CMakeFiles/saqut.dir
|
||||||
|
|
||||||
|
build CMakeFiles/saqut.dir/src/ir/ir_generator.cpp.o: CXX_COMPILER__saqut_unscanned_Debug /home/saqut/Masaüstü/saqutcompiler/src/ir/ir_generator.cpp || cmake_object_order_depends_target_saqut
|
||||||
|
CONFIG = Debug
|
||||||
|
DEP_FILE = CMakeFiles/saqut.dir/src/ir/ir_generator.cpp.o.d
|
||||||
|
FLAGS = -g -std=gnu++20 -Wall -Wextra -g -O0
|
||||||
|
INCLUDES = -I/home/saqut/Masaüstü/saqutcompiler/src
|
||||||
|
OBJECT_DIR = CMakeFiles/saqut.dir
|
||||||
|
OBJECT_FILE_DIR = CMakeFiles/saqut.dir/src/ir
|
||||||
|
TARGET_SUPPORT_DIR = CMakeFiles/saqut.dir
|
||||||
|
|
||||||
|
build CMakeFiles/saqut.dir/src/ir/ir_program.cpp.o: CXX_COMPILER__saqut_unscanned_Debug /home/saqut/Masaüstü/saqutcompiler/src/ir/ir_program.cpp || cmake_object_order_depends_target_saqut
|
||||||
|
CONFIG = Debug
|
||||||
|
DEP_FILE = CMakeFiles/saqut.dir/src/ir/ir_program.cpp.o.d
|
||||||
|
FLAGS = -g -std=gnu++20 -Wall -Wextra -g -O0
|
||||||
|
INCLUDES = -I/home/saqut/Masaüstü/saqutcompiler/src
|
||||||
|
OBJECT_DIR = CMakeFiles/saqut.dir
|
||||||
|
OBJECT_FILE_DIR = CMakeFiles/saqut.dir/src/ir
|
||||||
|
TARGET_SUPPORT_DIR = CMakeFiles/saqut.dir
|
||||||
|
|
||||||
|
build CMakeFiles/saqut.dir/src/lexer/lexer.cpp.o: CXX_COMPILER__saqut_unscanned_Debug /home/saqut/Masaüstü/saqutcompiler/src/lexer/lexer.cpp || cmake_object_order_depends_target_saqut
|
||||||
|
CONFIG = Debug
|
||||||
|
DEP_FILE = CMakeFiles/saqut.dir/src/lexer/lexer.cpp.o.d
|
||||||
|
FLAGS = -g -std=gnu++20 -Wall -Wextra -g -O0
|
||||||
|
INCLUDES = -I/home/saqut/Masaüstü/saqutcompiler/src
|
||||||
|
OBJECT_DIR = CMakeFiles/saqut.dir
|
||||||
|
OBJECT_FILE_DIR = CMakeFiles/saqut.dir/src/lexer
|
||||||
|
TARGET_SUPPORT_DIR = CMakeFiles/saqut.dir
|
||||||
|
|
||||||
|
build CMakeFiles/saqut.dir/src/main.cpp.o: CXX_COMPILER__saqut_unscanned_Debug /home/saqut/Masaüstü/saqutcompiler/src/main.cpp || cmake_object_order_depends_target_saqut
|
||||||
|
CONFIG = Debug
|
||||||
|
DEP_FILE = CMakeFiles/saqut.dir/src/main.cpp.o.d
|
||||||
|
FLAGS = -g -std=gnu++20 -Wall -Wextra -g -O0
|
||||||
|
INCLUDES = -I/home/saqut/Masaüstü/saqutcompiler/src
|
||||||
|
OBJECT_DIR = CMakeFiles/saqut.dir
|
||||||
|
OBJECT_FILE_DIR = CMakeFiles/saqut.dir/src
|
||||||
|
TARGET_SUPPORT_DIR = CMakeFiles/saqut.dir
|
||||||
|
|
||||||
|
build CMakeFiles/saqut.dir/src/parser/nodes/binary_expr.cpp.o: CXX_COMPILER__saqut_unscanned_Debug /home/saqut/Masaüstü/saqutcompiler/src/parser/nodes/binary_expr.cpp || cmake_object_order_depends_target_saqut
|
||||||
|
CONFIG = Debug
|
||||||
|
DEP_FILE = CMakeFiles/saqut.dir/src/parser/nodes/binary_expr.cpp.o.d
|
||||||
|
FLAGS = -g -std=gnu++20 -Wall -Wextra -g -O0
|
||||||
|
INCLUDES = -I/home/saqut/Masaüstü/saqutcompiler/src
|
||||||
|
OBJECT_DIR = CMakeFiles/saqut.dir
|
||||||
|
OBJECT_FILE_DIR = CMakeFiles/saqut.dir/src/parser/nodes
|
||||||
|
TARGET_SUPPORT_DIR = CMakeFiles/saqut.dir
|
||||||
|
|
||||||
|
build CMakeFiles/saqut.dir/src/parser/nodes/declarations.cpp.o: CXX_COMPILER__saqut_unscanned_Debug /home/saqut/Masaüstü/saqutcompiler/src/parser/nodes/declarations.cpp || cmake_object_order_depends_target_saqut
|
||||||
|
CONFIG = Debug
|
||||||
|
DEP_FILE = CMakeFiles/saqut.dir/src/parser/nodes/declarations.cpp.o.d
|
||||||
|
FLAGS = -g -std=gnu++20 -Wall -Wextra -g -O0
|
||||||
|
INCLUDES = -I/home/saqut/Masaüstü/saqutcompiler/src
|
||||||
|
OBJECT_DIR = CMakeFiles/saqut.dir
|
||||||
|
OBJECT_FILE_DIR = CMakeFiles/saqut.dir/src/parser/nodes
|
||||||
|
TARGET_SUPPORT_DIR = CMakeFiles/saqut.dir
|
||||||
|
|
||||||
|
build CMakeFiles/saqut.dir/src/parser/nodes/expressions.cpp.o: CXX_COMPILER__saqut_unscanned_Debug /home/saqut/Masaüstü/saqutcompiler/src/parser/nodes/expressions.cpp || cmake_object_order_depends_target_saqut
|
||||||
|
CONFIG = Debug
|
||||||
|
DEP_FILE = CMakeFiles/saqut.dir/src/parser/nodes/expressions.cpp.o.d
|
||||||
|
FLAGS = -g -std=gnu++20 -Wall -Wextra -g -O0
|
||||||
|
INCLUDES = -I/home/saqut/Masaüstü/saqutcompiler/src
|
||||||
|
OBJECT_DIR = CMakeFiles/saqut.dir
|
||||||
|
OBJECT_FILE_DIR = CMakeFiles/saqut.dir/src/parser/nodes
|
||||||
|
TARGET_SUPPORT_DIR = CMakeFiles/saqut.dir
|
||||||
|
|
||||||
|
build CMakeFiles/saqut.dir/src/parser/nodes/identifier.cpp.o: CXX_COMPILER__saqut_unscanned_Debug /home/saqut/Masaüstü/saqutcompiler/src/parser/nodes/identifier.cpp || cmake_object_order_depends_target_saqut
|
||||||
|
CONFIG = Debug
|
||||||
|
DEP_FILE = CMakeFiles/saqut.dir/src/parser/nodes/identifier.cpp.o.d
|
||||||
|
FLAGS = -g -std=gnu++20 -Wall -Wextra -g -O0
|
||||||
|
INCLUDES = -I/home/saqut/Masaüstü/saqutcompiler/src
|
||||||
|
OBJECT_DIR = CMakeFiles/saqut.dir
|
||||||
|
OBJECT_FILE_DIR = CMakeFiles/saqut.dir/src/parser/nodes
|
||||||
|
TARGET_SUPPORT_DIR = CMakeFiles/saqut.dir
|
||||||
|
|
||||||
|
build CMakeFiles/saqut.dir/src/parser/nodes/literal.cpp.o: CXX_COMPILER__saqut_unscanned_Debug /home/saqut/Masaüstü/saqutcompiler/src/parser/nodes/literal.cpp || cmake_object_order_depends_target_saqut
|
||||||
|
CONFIG = Debug
|
||||||
|
DEP_FILE = CMakeFiles/saqut.dir/src/parser/nodes/literal.cpp.o.d
|
||||||
|
FLAGS = -g -std=gnu++20 -Wall -Wextra -g -O0
|
||||||
|
INCLUDES = -I/home/saqut/Masaüstü/saqutcompiler/src
|
||||||
|
OBJECT_DIR = CMakeFiles/saqut.dir
|
||||||
|
OBJECT_FILE_DIR = CMakeFiles/saqut.dir/src/parser/nodes
|
||||||
|
TARGET_SUPPORT_DIR = CMakeFiles/saqut.dir
|
||||||
|
|
||||||
|
build CMakeFiles/saqut.dir/src/parser/nodes/program.cpp.o: CXX_COMPILER__saqut_unscanned_Debug /home/saqut/Masaüstü/saqutcompiler/src/parser/nodes/program.cpp || cmake_object_order_depends_target_saqut
|
||||||
|
CONFIG = Debug
|
||||||
|
DEP_FILE = CMakeFiles/saqut.dir/src/parser/nodes/program.cpp.o.d
|
||||||
|
FLAGS = -g -std=gnu++20 -Wall -Wextra -g -O0
|
||||||
|
INCLUDES = -I/home/saqut/Masaüstü/saqutcompiler/src
|
||||||
|
OBJECT_DIR = CMakeFiles/saqut.dir
|
||||||
|
OBJECT_FILE_DIR = CMakeFiles/saqut.dir/src/parser/nodes
|
||||||
|
TARGET_SUPPORT_DIR = CMakeFiles/saqut.dir
|
||||||
|
|
||||||
|
build CMakeFiles/saqut.dir/src/parser/nodes/statements.cpp.o: CXX_COMPILER__saqut_unscanned_Debug /home/saqut/Masaüstü/saqutcompiler/src/parser/nodes/statements.cpp || cmake_object_order_depends_target_saqut
|
||||||
|
CONFIG = Debug
|
||||||
|
DEP_FILE = CMakeFiles/saqut.dir/src/parser/nodes/statements.cpp.o.d
|
||||||
|
FLAGS = -g -std=gnu++20 -Wall -Wextra -g -O0
|
||||||
|
INCLUDES = -I/home/saqut/Masaüstü/saqutcompiler/src
|
||||||
|
OBJECT_DIR = CMakeFiles/saqut.dir
|
||||||
|
OBJECT_FILE_DIR = CMakeFiles/saqut.dir/src/parser/nodes
|
||||||
|
TARGET_SUPPORT_DIR = CMakeFiles/saqut.dir
|
||||||
|
|
||||||
|
build CMakeFiles/saqut.dir/src/parser/parser.cpp.o: CXX_COMPILER__saqut_unscanned_Debug /home/saqut/Masaüstü/saqutcompiler/src/parser/parser.cpp || cmake_object_order_depends_target_saqut
|
||||||
|
CONFIG = Debug
|
||||||
|
DEP_FILE = CMakeFiles/saqut.dir/src/parser/parser.cpp.o.d
|
||||||
|
FLAGS = -g -std=gnu++20 -Wall -Wextra -g -O0
|
||||||
|
INCLUDES = -I/home/saqut/Masaüstü/saqutcompiler/src
|
||||||
|
OBJECT_DIR = CMakeFiles/saqut.dir
|
||||||
|
OBJECT_FILE_DIR = CMakeFiles/saqut.dir/src/parser
|
||||||
|
TARGET_SUPPORT_DIR = CMakeFiles/saqut.dir
|
||||||
|
|
||||||
|
build CMakeFiles/saqut.dir/src/semantic/structural_validator.cpp.o: CXX_COMPILER__saqut_unscanned_Debug /home/saqut/Masaüstü/saqutcompiler/src/semantic/structural_validator.cpp || cmake_object_order_depends_target_saqut
|
||||||
|
CONFIG = Debug
|
||||||
|
DEP_FILE = CMakeFiles/saqut.dir/src/semantic/structural_validator.cpp.o.d
|
||||||
|
FLAGS = -g -std=gnu++20 -Wall -Wextra -g -O0
|
||||||
|
INCLUDES = -I/home/saqut/Masaüstü/saqutcompiler/src
|
||||||
|
OBJECT_DIR = CMakeFiles/saqut.dir
|
||||||
|
OBJECT_FILE_DIR = CMakeFiles/saqut.dir/src/semantic
|
||||||
|
TARGET_SUPPORT_DIR = CMakeFiles/saqut.dir
|
||||||
|
|
||||||
|
build CMakeFiles/saqut.dir/src/semantic/type_checker.cpp.o: CXX_COMPILER__saqut_unscanned_Debug /home/saqut/Masaüstü/saqutcompiler/src/semantic/type_checker.cpp || cmake_object_order_depends_target_saqut
|
||||||
|
CONFIG = Debug
|
||||||
|
DEP_FILE = CMakeFiles/saqut.dir/src/semantic/type_checker.cpp.o.d
|
||||||
|
FLAGS = -g -std=gnu++20 -Wall -Wextra -g -O0
|
||||||
|
INCLUDES = -I/home/saqut/Masaüstü/saqutcompiler/src
|
||||||
|
OBJECT_DIR = CMakeFiles/saqut.dir
|
||||||
|
OBJECT_FILE_DIR = CMakeFiles/saqut.dir/src/semantic
|
||||||
|
TARGET_SUPPORT_DIR = CMakeFiles/saqut.dir
|
||||||
|
|
||||||
|
build CMakeFiles/saqut.dir/src/symbol/symbol_collector.cpp.o: CXX_COMPILER__saqut_unscanned_Debug /home/saqut/Masaüstü/saqutcompiler/src/symbol/symbol_collector.cpp || cmake_object_order_depends_target_saqut
|
||||||
|
CONFIG = Debug
|
||||||
|
DEP_FILE = CMakeFiles/saqut.dir/src/symbol/symbol_collector.cpp.o.d
|
||||||
|
FLAGS = -g -std=gnu++20 -Wall -Wextra -g -O0
|
||||||
|
INCLUDES = -I/home/saqut/Masaüstü/saqutcompiler/src
|
||||||
|
OBJECT_DIR = CMakeFiles/saqut.dir
|
||||||
|
OBJECT_FILE_DIR = CMakeFiles/saqut.dir/src/symbol
|
||||||
|
TARGET_SUPPORT_DIR = CMakeFiles/saqut.dir
|
||||||
|
|
||||||
|
build CMakeFiles/saqut.dir/src/tokenizer/tokenizer.cpp.o: CXX_COMPILER__saqut_unscanned_Debug /home/saqut/Masaüstü/saqutcompiler/src/tokenizer/tokenizer.cpp || cmake_object_order_depends_target_saqut
|
||||||
|
CONFIG = Debug
|
||||||
|
DEP_FILE = CMakeFiles/saqut.dir/src/tokenizer/tokenizer.cpp.o.d
|
||||||
|
FLAGS = -g -std=gnu++20 -Wall -Wextra -g -O0
|
||||||
|
INCLUDES = -I/home/saqut/Masaüstü/saqutcompiler/src
|
||||||
|
OBJECT_DIR = CMakeFiles/saqut.dir
|
||||||
|
OBJECT_FILE_DIR = CMakeFiles/saqut.dir/src/tokenizer
|
||||||
|
TARGET_SUPPORT_DIR = CMakeFiles/saqut.dir
|
||||||
|
|
||||||
|
build CMakeFiles/saqut.dir/src/vm/interpreter.cpp.o: CXX_COMPILER__saqut_unscanned_Debug /home/saqut/Masaüstü/saqutcompiler/src/vm/interpreter.cpp || cmake_object_order_depends_target_saqut
|
||||||
|
CONFIG = Debug
|
||||||
|
DEP_FILE = CMakeFiles/saqut.dir/src/vm/interpreter.cpp.o.d
|
||||||
|
FLAGS = -g -std=gnu++20 -Wall -Wextra -g -O0
|
||||||
|
INCLUDES = -I/home/saqut/Masaüstü/saqutcompiler/src
|
||||||
|
OBJECT_DIR = CMakeFiles/saqut.dir
|
||||||
|
OBJECT_FILE_DIR = CMakeFiles/saqut.dir/src/vm
|
||||||
|
TARGET_SUPPORT_DIR = CMakeFiles/saqut.dir
|
||||||
|
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# Link build statements for EXECUTABLE target saqut
|
||||||
|
|
||||||
|
|
||||||
|
#############################################
|
||||||
|
# Link the executable saqut
|
||||||
|
|
||||||
|
build saqut: CXX_EXECUTABLE_LINKER__saqut_Debug CMakeFiles/saqut.dir/src/core/sourcefile.cpp.o CMakeFiles/saqut.dir/src/ir/ir_function.cpp.o CMakeFiles/saqut.dir/src/ir/ir_generator.cpp.o CMakeFiles/saqut.dir/src/ir/ir_program.cpp.o CMakeFiles/saqut.dir/src/lexer/lexer.cpp.o CMakeFiles/saqut.dir/src/main.cpp.o CMakeFiles/saqut.dir/src/parser/nodes/binary_expr.cpp.o CMakeFiles/saqut.dir/src/parser/nodes/declarations.cpp.o CMakeFiles/saqut.dir/src/parser/nodes/expressions.cpp.o CMakeFiles/saqut.dir/src/parser/nodes/identifier.cpp.o CMakeFiles/saqut.dir/src/parser/nodes/literal.cpp.o CMakeFiles/saqut.dir/src/parser/nodes/program.cpp.o CMakeFiles/saqut.dir/src/parser/nodes/statements.cpp.o CMakeFiles/saqut.dir/src/parser/parser.cpp.o CMakeFiles/saqut.dir/src/semantic/structural_validator.cpp.o CMakeFiles/saqut.dir/src/semantic/type_checker.cpp.o CMakeFiles/saqut.dir/src/symbol/symbol_collector.cpp.o CMakeFiles/saqut.dir/src/tokenizer/tokenizer.cpp.o CMakeFiles/saqut.dir/src/vm/interpreter.cpp.o
|
||||||
|
CONFIG = Debug
|
||||||
|
DEP_FILE = CMakeFiles/saqut.dir/link.d
|
||||||
|
FLAGS = -g
|
||||||
|
LINK_FLAGS = -Wl,--dependency-file=CMakeFiles/saqut.dir/link.d
|
||||||
|
OBJECT_DIR = CMakeFiles/saqut.dir
|
||||||
|
POST_BUILD = :
|
||||||
|
PRE_LINK = :
|
||||||
|
TARGET_FILE = saqut
|
||||||
|
TARGET_PDB = saqut.dbg
|
||||||
|
TARGET_SUPPORT_DIR = CMakeFiles/saqut.dir
|
||||||
|
|
||||||
|
|
||||||
|
#############################################
|
||||||
|
# Utility command for edit_cache
|
||||||
|
|
||||||
|
build CMakeFiles/edit_cache.util: CUSTOM_COMMAND
|
||||||
|
COMMAND = cd /home/saqut/Masaüstü/saqutcompiler/build && /usr/bin/ccmake -S/home/saqut/Masaüstü/saqutcompiler -B/home/saqut/Masaüstü/saqutcompiler/build
|
||||||
|
DESC = Running CMake cache editor...
|
||||||
|
pool = console
|
||||||
|
restat = 1
|
||||||
|
|
||||||
|
build edit_cache: phony CMakeFiles/edit_cache.util
|
||||||
|
|
||||||
|
|
||||||
|
#############################################
|
||||||
|
# Utility command for rebuild_cache
|
||||||
|
|
||||||
|
build CMakeFiles/rebuild_cache.util: CUSTOM_COMMAND
|
||||||
|
COMMAND = cd /home/saqut/Masaüstü/saqutcompiler/build && /usr/bin/cmake --regenerate-during-build -S/home/saqut/Masaüstü/saqutcompiler -B/home/saqut/Masaüstü/saqutcompiler/build
|
||||||
|
DESC = Running CMake to regenerate build system...
|
||||||
|
pool = console
|
||||||
|
restat = 1
|
||||||
|
|
||||||
|
build rebuild_cache: phony CMakeFiles/rebuild_cache.util
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# Target aliases.
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# Folder targets.
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
#############################################
|
||||||
|
# Folder: /home/saqut/Masaüstü/saqutcompiler/build
|
||||||
|
|
||||||
|
build all: phony saqut
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# Built-in targets
|
||||||
|
|
||||||
|
|
||||||
|
#############################################
|
||||||
|
# Re-run CMake if any of its inputs changed.
|
||||||
|
|
||||||
|
build build.ninja /home/saqut/Masaüstü/saqutcompiler/build/cmake_install.cmake: RERUN_CMAKE | /home/saqut/Masaüstü/saqutcompiler/CMakeLists.txt /usr/share/cmake/Modules/CMakeCXXInformation.cmake /usr/share/cmake/Modules/CMakeCommonLanguageInclude.cmake /usr/share/cmake/Modules/CMakeGenericSystem.cmake /usr/share/cmake/Modules/CMakeInitializeConfigs.cmake /usr/share/cmake/Modules/CMakeLanguageInformation.cmake /usr/share/cmake/Modules/CMakeSystemSpecificInformation.cmake /usr/share/cmake/Modules/CMakeSystemSpecificInitialize.cmake /usr/share/cmake/Modules/Compiler/CMakeCommonCompilerMacros.cmake /usr/share/cmake/Modules/Compiler/GNU-CXX.cmake /usr/share/cmake/Modules/Compiler/GNU.cmake /usr/share/cmake/Modules/Internal/CMakeCXXLinkerInformation.cmake /usr/share/cmake/Modules/Internal/CMakeCommonLinkerInformation.cmake /usr/share/cmake/Modules/Linker/GNU-CXX.cmake /usr/share/cmake/Modules/Linker/GNU.cmake /usr/share/cmake/Modules/Platform/Linker/GNU.cmake /usr/share/cmake/Modules/Platform/Linker/Linux-GNU-CXX.cmake /usr/share/cmake/Modules/Platform/Linker/Linux-GNU.cmake /usr/share/cmake/Modules/Platform/Linux-GNU-CXX.cmake /usr/share/cmake/Modules/Platform/Linux-GNU.cmake /usr/share/cmake/Modules/Platform/Linux-Initialize.cmake /usr/share/cmake/Modules/Platform/Linux.cmake /usr/share/cmake/Modules/Platform/UnixPaths.cmake CMakeCache.txt CMakeFiles/4.3.3/CMakeCXXCompiler.cmake CMakeFiles/4.3.3/CMakeSystem.cmake
|
||||||
|
pool = console
|
||||||
|
|
||||||
|
|
||||||
|
#############################################
|
||||||
|
# A missing CMake input file is not an error.
|
||||||
|
|
||||||
|
build /home/saqut/Masaüstü/saqutcompiler/CMakeLists.txt /usr/share/cmake/Modules/CMakeCXXInformation.cmake /usr/share/cmake/Modules/CMakeCommonLanguageInclude.cmake /usr/share/cmake/Modules/CMakeGenericSystem.cmake /usr/share/cmake/Modules/CMakeInitializeConfigs.cmake /usr/share/cmake/Modules/CMakeLanguageInformation.cmake /usr/share/cmake/Modules/CMakeSystemSpecificInformation.cmake /usr/share/cmake/Modules/CMakeSystemSpecificInitialize.cmake /usr/share/cmake/Modules/Compiler/CMakeCommonCompilerMacros.cmake /usr/share/cmake/Modules/Compiler/GNU-CXX.cmake /usr/share/cmake/Modules/Compiler/GNU.cmake /usr/share/cmake/Modules/Internal/CMakeCXXLinkerInformation.cmake /usr/share/cmake/Modules/Internal/CMakeCommonLinkerInformation.cmake /usr/share/cmake/Modules/Linker/GNU-CXX.cmake /usr/share/cmake/Modules/Linker/GNU.cmake /usr/share/cmake/Modules/Platform/Linker/GNU.cmake /usr/share/cmake/Modules/Platform/Linker/Linux-GNU-CXX.cmake /usr/share/cmake/Modules/Platform/Linker/Linux-GNU.cmake /usr/share/cmake/Modules/Platform/Linux-GNU-CXX.cmake /usr/share/cmake/Modules/Platform/Linux-GNU.cmake /usr/share/cmake/Modules/Platform/Linux-Initialize.cmake /usr/share/cmake/Modules/Platform/Linux.cmake /usr/share/cmake/Modules/Platform/UnixPaths.cmake CMakeCache.txt CMakeFiles/4.3.3/CMakeCXXCompiler.cmake CMakeFiles/4.3.3/CMakeSystem.cmake: phony
|
||||||
|
|
||||||
|
|
||||||
|
#############################################
|
||||||
|
# Clean all the built files.
|
||||||
|
|
||||||
|
build clean: CLEAN
|
||||||
|
|
||||||
|
|
||||||
|
#############################################
|
||||||
|
# Print all primary targets available.
|
||||||
|
|
||||||
|
build help: HELP
|
||||||
|
|
||||||
|
|
||||||
|
#############################################
|
||||||
|
# Make the all target the default.
|
||||||
|
|
||||||
|
default all
|
||||||
|
|
@ -0,0 +1,66 @@
|
||||||
|
# Install script for directory: /home/saqut/Masaüstü/saqutcompiler
|
||||||
|
|
||||||
|
# Set the install prefix
|
||||||
|
if(NOT DEFINED CMAKE_INSTALL_PREFIX)
|
||||||
|
set(CMAKE_INSTALL_PREFIX "/usr/local")
|
||||||
|
endif()
|
||||||
|
string(REGEX REPLACE "/$" "" CMAKE_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}")
|
||||||
|
|
||||||
|
# Set the install configuration name.
|
||||||
|
if(NOT DEFINED CMAKE_INSTALL_CONFIG_NAME)
|
||||||
|
if(BUILD_TYPE)
|
||||||
|
string(REGEX REPLACE "^[^A-Za-z0-9_]+" ""
|
||||||
|
CMAKE_INSTALL_CONFIG_NAME "${BUILD_TYPE}")
|
||||||
|
else()
|
||||||
|
set(CMAKE_INSTALL_CONFIG_NAME "Debug")
|
||||||
|
endif()
|
||||||
|
message(STATUS "Install configuration: \"${CMAKE_INSTALL_CONFIG_NAME}\"")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Set the component getting installed.
|
||||||
|
if(NOT CMAKE_INSTALL_COMPONENT)
|
||||||
|
if(COMPONENT)
|
||||||
|
message(STATUS "Install component: \"${COMPONENT}\"")
|
||||||
|
set(CMAKE_INSTALL_COMPONENT "${COMPONENT}")
|
||||||
|
else()
|
||||||
|
set(CMAKE_INSTALL_COMPONENT)
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Install shared libraries without execute permission?
|
||||||
|
if(NOT DEFINED CMAKE_INSTALL_SO_NO_EXE)
|
||||||
|
set(CMAKE_INSTALL_SO_NO_EXE "0")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Is this installation the result of a crosscompile?
|
||||||
|
if(NOT DEFINED CMAKE_CROSSCOMPILING)
|
||||||
|
set(CMAKE_CROSSCOMPILING "FALSE")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Set path to fallback-tool for dependency-resolution.
|
||||||
|
if(NOT DEFINED CMAKE_OBJDUMP)
|
||||||
|
set(CMAKE_OBJDUMP "/usr/bin/objdump")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
string(REPLACE ";" "\n" CMAKE_INSTALL_MANIFEST_CONTENT
|
||||||
|
"${CMAKE_INSTALL_MANIFEST_FILES}")
|
||||||
|
if(CMAKE_INSTALL_LOCAL_ONLY)
|
||||||
|
file(WRITE "/home/saqut/Masaüstü/saqutcompiler/build/install_local_manifest.txt"
|
||||||
|
"${CMAKE_INSTALL_MANIFEST_CONTENT}")
|
||||||
|
endif()
|
||||||
|
if(CMAKE_INSTALL_COMPONENT)
|
||||||
|
if(CMAKE_INSTALL_COMPONENT MATCHES "^[a-zA-Z0-9_.+-]+$")
|
||||||
|
set(CMAKE_INSTALL_MANIFEST "install_manifest_${CMAKE_INSTALL_COMPONENT}.txt")
|
||||||
|
else()
|
||||||
|
string(MD5 CMAKE_INST_COMP_HASH "${CMAKE_INSTALL_COMPONENT}")
|
||||||
|
set(CMAKE_INSTALL_MANIFEST "install_manifest_${CMAKE_INST_COMP_HASH}.txt")
|
||||||
|
unset(CMAKE_INST_COMP_HASH)
|
||||||
|
endif()
|
||||||
|
else()
|
||||||
|
set(CMAKE_INSTALL_MANIFEST "install_manifest.txt")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(NOT CMAKE_INSTALL_LOCAL_ONLY)
|
||||||
|
file(WRITE "/home/saqut/Masaüstü/saqutcompiler/build/${CMAKE_INSTALL_MANIFEST}"
|
||||||
|
"${CMAKE_INSTALL_MANIFEST_CONTENT}")
|
||||||
|
endif()
|
||||||
|
|
@ -0,0 +1,682 @@
|
||||||
|
# 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`.
|
||||||
|
>
|
||||||
|
> ⚠️ **Yapılan vs planlanan:** Bu belgedeki ADR-006…019 **tasarım kararlarıdır**;
|
||||||
|
> tarif edilen makine (sembol tablosu, semantik analiz, tip sistemi, diagnostic,
|
||||||
|
> optimizasyon, IR+VM) **henüz kodlanmamıştır.** Bugün çalışan: lexer, tokenizer,
|
||||||
|
> Pratt parser, AST, AST'nin JSON serileştirmesi, CLI iskeleti, konum takibi ve
|
||||||
|
> basit aritmetiği düşüren minimal bir IR deneyi. Hiçbir ADR, var olmayan bir
|
||||||
|
> mekanizmayı varmış gibi anlatmaz.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 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, bytecode VM (birincil)
|
||||||
|
symbol table → toggle'lı, ortak + ileride C transpile
|
||||||
|
semantic analiz gösterim üstünde) (makine kodu = uzak
|
||||||
|
(annotated AST) gelecek; ADR-015)
|
||||||
|
```
|
||||||
|
|
||||||
|
- **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 (birincil: IR+bytecode VM, ADR-015;
|
||||||
|
ileride: C transpile; çok uzak: makine kodu) hedeflendiği 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)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Güncelleme — Klon maliyeti yük taşır (load-bearing)
|
||||||
|
|
||||||
|
İlk metin "ağaç klonlamak ucuz ve basittir" diyordu; bu **klon maliyetini hafife
|
||||||
|
alıyor** ve bir **tutarlılık (coherence) problemini** atlıyordu. Düzeltme:
|
||||||
|
|
||||||
|
`ASTNode::clone()` "belki gerekir" değil, **merkezi ve spesifiye edilmesi
|
||||||
|
zorunlu** bir bileşendir; tüm öncesi/sonrası hikâyesi ona dayanır (bkz. roadmap
|
||||||
|
Faz 4'te clone() yükseltildi).
|
||||||
|
|
||||||
|
**Klonlanırken karar verilmesi gereken iki nokta (açıkça belgele):**
|
||||||
|
|
||||||
|
1. **Parent pointer'lar yeniden bağlanmalı.** Klon node'larının `parent`'ı
|
||||||
|
orijinali değil, klonu göstermeli; yoksa yapısal doğrulama ve dönüşümler
|
||||||
|
yanlış ağaçta gezinir.
|
||||||
|
|
||||||
|
2. **`IdentifierNode → Symbol` bağları: paylaş mı, yeniden eşle mi?**
|
||||||
|
- **Paylaş** (klon ve orijinal aynı sembol tablosuna işaret eder): ucuz, ama
|
||||||
|
klonu optimize etmek orijinalin **referans sayımlarını bozar** (DCE klonda
|
||||||
|
bir kullanımı silince orijinalin Symbol ref-count'u da düşer).
|
||||||
|
- **Yeniden eşle** (klona ait bir sembol tablosu kopyası): doğru, ama ucuz
|
||||||
|
değil.
|
||||||
|
- **Karar:** `--optimized` istendiğinde sembol tablosu da **klonlanır ve
|
||||||
|
yeniden eşlenir** (remap). Doğruluk, ucuzluğa tercih edilir; klon zaten
|
||||||
|
yalnızca optimizasyon istendiğinde üretilir, sıcak yol değildir. "Ucuz"
|
||||||
|
iddiası kaldırıldı.
|
||||||
|
|
||||||
|
Bu, ADR-013'teki "ref-count Symbol'da yaşar" kararıyla tutarlıdır: ref-count
|
||||||
|
Symbol'da olduğu için, klonun kendi Symbol'larına sahip olması şarttır.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 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 → bytecode VM ve ileride C transpile 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.
|
||||||
|
|
||||||
|
### Güncelleme — Sonlanma değişmezi (termination invariant)
|
||||||
|
|
||||||
|
Fixpoint döngüsünün **sonlanacağı garanti edilmeli**. İki seçenekten en az biri
|
||||||
|
zorunludur:
|
||||||
|
|
||||||
|
1. **Monotonluk:** havuzdaki tüm pass'ler **monoton** olmalı — yalnızca
|
||||||
|
küçültür/sadeleştirir, asla büyütmez. Constant folding ve dead code
|
||||||
|
elimination bugün monotondur, dolayısıyla fixpoint sonlanır.
|
||||||
|
2. **Sert iterasyon tavanı (cap):** bir üst sınır (örn. `maxFixpointRounds`).
|
||||||
|
|
||||||
|
**Neden gerekli:** ileride **büyüten** pass'ler (inlining, loop unrolling)
|
||||||
|
eklenirse, naif fixpoint **salınabilir** (A büyütür, B küçültür, sonsuz döngü).
|
||||||
|
Büyüten bir pass eklendiği an, monotonluk bozulur ve **iterasyon tavanı zorunlu
|
||||||
|
hale gelir.** Bu değişmez şimdiden yazıya geçirildi ki ileride unutulmasın.
|
||||||
|
|
||||||
|
### Güncelleme — "Analiz bir kez çalışır" çelişkisinin çözümü
|
||||||
|
|
||||||
|
ADR-013 "analiz bir kez çalışır" diyor; ama folding **erişilebilirliği**
|
||||||
|
(`if(false)`) ve **referans sayımlarını** değiştirir, DCE de tam bunlara
|
||||||
|
dayanır. Eğer analiz gerçekten yalnızca bir kez çalışırsa, fixpoint'in ikinci
|
||||||
|
turundaki DCE **bayat (stale) veriyle** çalışır ve zincirleme fırsatları kaçırır.
|
||||||
|
|
||||||
|
**Çözüm — iki analiz sınıfını ayır:**
|
||||||
|
|
||||||
|
- **Kaynağa-bağlı analiz** (her ifadenin tipi, sembol bağları): kaynak değişmediği
|
||||||
|
sürece sabittir → **bir kez** çalışır, klona taşınır.
|
||||||
|
- **Türetilmiş/akışa-bağlı analiz** (erişilebilirlik `isReachable`, referans
|
||||||
|
sayıları): bir dönüşüm bunları geçersizleştirir → **fixpoint döngüsünün her
|
||||||
|
turunda, klon üzerinde yeniden hesaplanır.**
|
||||||
|
|
||||||
|
Yani "analiz bir kez çalışır" ifadesi yalnızca **kaynağa-bağlı** analiz için
|
||||||
|
geçerlidir; akışa-bağlı analiz tur başına tazelenir. ADR-013 buna göre okunmalı.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 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ı.
|
||||||
|
|
||||||
|
### Güncelleme — Sayısal literal tipleme kuralı
|
||||||
|
|
||||||
|
"Gizli dönüşüm yok + tip çıkarımı yok" altında `float x = 1;` ifadesi
|
||||||
|
**tanımsızdı**. Bu açıkça karara bağlanmalı, çünkü tip denetleyicisini (Faz 3)
|
||||||
|
doğrudan yönlendirir.
|
||||||
|
|
||||||
|
**Değerlendirilen iki kural:**
|
||||||
|
|
||||||
|
- **(a) Literal her zaman `int`:** `1` daima `int`'tir. `float x = 1;` bir tip
|
||||||
|
hatasıdır; `float x = 1.0;` yazmak zorunludur. En katı, en öngörülebilir; ama
|
||||||
|
rahatsız edici ve "gizli dönüşüm yok" ilkesini literallere kadar gereksiz yere
|
||||||
|
zorlar.
|
||||||
|
- **(b) Tamsayı literali bağlama-göre tiplenir (context-typed / polymorphic):**
|
||||||
|
tipsiz bir tamsayı sabiti, beklenen tip ona **kayıpsız** sığıyorsa o tipe
|
||||||
|
uyarlanır. `float x = 1;` çalışır (`1` → `1.0`); `int y = 1.5;` ise hata
|
||||||
|
(kayıp olur).
|
||||||
|
|
||||||
|
**Karar:** ✅ **(b) Bağlama-göre tiplenen tamsayı literalleri.**
|
||||||
|
|
||||||
|
- Gerekçe: bu bir **değişken-değer dönüşümü değil, bir derleme-zamanı sabitinin
|
||||||
|
uygun tipte yorumlanmasıdır** — tam olarak ADR-010'un zaten tanıdığı "sabit
|
||||||
|
istisnası" (`int a = 5/2 → 2`) ruhuyla aynı kapıya çıkar. Çalışma zamanı
|
||||||
|
`int` değişkenini `float`'a gizlice çevirmek hâlâ **yasaktır**; istisna
|
||||||
|
yalnızca **literal/sabit** içindir.
|
||||||
|
- Kural net: *değişken→değişken* gizli dönüşüm yok; *literal→beklenen tip*
|
||||||
|
kayıpsızsa serbest. `float x = anInt;` hata; `float x = 1;` serbest.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 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).
|
||||||
|
|
||||||
|
### Güncelleme — Global "tam forward reference" çok genişti: üç-parçalı kural
|
||||||
|
|
||||||
|
İlk metin "global = her zaman forward-reference güvenli" diyordu; bu **fazla
|
||||||
|
geniş**. Global bir değişkenin **başlatıcısının (initializer) bir çalışma
|
||||||
|
sırası vardır** (tıpkı lokaller gibi). Düzeltme — üç ayrı kural:
|
||||||
|
|
||||||
|
1. **Global fonksiyonlar / struct'lar → tam hoisting.** Tanım anında çalışmazlar,
|
||||||
|
sıradan bağımsız her yerde görünür. (Güvenli; değişmedi.)
|
||||||
|
2. **Global değişken isimleri → hoist edilir.** İsim her yerde görünür.
|
||||||
|
3. **Global değişken başlatıcıları → değer sırasına tabidir** (lokaller gibi) →
|
||||||
|
**declare-before-use** VEYA bir **definite-assignment (kesin-atama) analizi**
|
||||||
|
gerektirir.
|
||||||
|
|
||||||
|
**Neden:** `int a = b; int b = 5;` global scope'ta, isim-hoisting'e güvenilirse,
|
||||||
|
`a`'ya **sessizce çöp değer** verir — kaçınmaya çalıştığımız tam o JS `var`
|
||||||
|
durumu. Java da aynı sebeple bunu kısıtlar. Karar: global başlatıcılar için de
|
||||||
|
**declare-before-use** uygulanır (en basit, definite-assignment'a gerek
|
||||||
|
bırakmaz). Yani isim görünür ama **kendinden önceki** bir global başlatıcıda
|
||||||
|
kullanılabilir.
|
||||||
|
|
||||||
|
### Güncelleme — Döngüsel / karşılıklı-özyinelemeli struct tespiti
|
||||||
|
|
||||||
|
Pointer olmadığı için tüm struct iç içeliği **değer (by-value)** ile olur →
|
||||||
|
herhangi bir kapsama döngüsü sonsuz boyut demektir:
|
||||||
|
|
||||||
|
```
|
||||||
|
struct A { B b } // A, B'yi değer olarak içerir
|
||||||
|
struct B { A a } // B, A'yı değer olarak içerir → sonsuz boyut
|
||||||
|
```
|
||||||
|
|
||||||
|
Bu **derleme hatası olmak zorunda** ve hata kataloğunda **eksikti**. Eklendi:
|
||||||
|
**`E010` — özyinelemeli/döngüsel struct tanımı.** Symbol toplama sonrası bir
|
||||||
|
**topolojik / kapsama-döngüsü kontrolü** çalışır (struct'ları düğüm, "alan
|
||||||
|
olarak içerir" kenarını çevrim arayan bir DFS ile). Çevrim bulunursa `E010`.
|
||||||
|
|
||||||
|
(Karşılaştır: `struct A { B b }` + `struct B { int x }` geçerlidir; yalnızca
|
||||||
|
**çevrim** yasaktır. Pointer olsaydı çevrim mümkün olurdu — ama pointer yok.)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 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 / kalıtım | ❌ Yok (başta) | `class` keyword'ü yok sayılır |
|
||||||
|
| Closure | ❌ Yok | Bkz. ADR-019 (bellek bağımlılığı) |
|
||||||
|
| Struct | ✅ Var | `struct A { B bVar }` olur (B başka yerde tanımlı); **çevrim yasak → `E010`** |
|
||||||
|
| `interface` | ⏸️ Ertelendi | Reddedilmedi; v0 değil — gerekçe aşağıda + ADR-018 |
|
||||||
|
| 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/literal folding'de istisna (ADR-010) |
|
||||||
|
|
||||||
|
### 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.
|
||||||
|
- **IR + bytecode VM (ilk çalıştırma modeli, ADR-015):** bellek host (C++)
|
||||||
|
heap'idir; array'ler host tarafında `std::vector` benzeri bir yapıyla tutulur.
|
||||||
|
VM, array işlemleri için **host fonksiyonlarına** (FFI seam, ADR-016) çağrı
|
||||||
|
yapar. v0 için özel allocator gerekmez.
|
||||||
|
- **C transpile backend (ileride, ikinci backend):** `int[]` → C'de
|
||||||
|
`struct {int* data; size_t len, cap;}`, `malloc/realloc/free` ile yönetilir.
|
||||||
|
- **Yönetim stratejisi (ne zaman free):** scope-tabanlı ownership (array'i tutan
|
||||||
|
değişken scope'tan çıkınca free). GC gerekmez — **neden gerekmediği aşağıda
|
||||||
|
gerekçelendirildi.**
|
||||||
|
|
||||||
|
### Güncelleme — Scope-tabanlı bellek artık GEREKÇELİ (bağımlılığı belgele)
|
||||||
|
|
||||||
|
Önceki kaygı ("scope çıkışında free, aliasing/escape altında bozulur") kilitli
|
||||||
|
dil kimliğiyle **lehte çözüldü:**
|
||||||
|
|
||||||
|
> prosedürel + value semantics + kullanıcı pointer'ı yok + closure yok + kaçan
|
||||||
|
> referans yok → array/string'ler tanımlandıkları scope'tan **kaçamaz** →
|
||||||
|
> scope-tabanlı ownership (scope çıkışında free) **gerçekten çalışır, GC
|
||||||
|
> gerekmez.**
|
||||||
|
|
||||||
|
**Bağımlılık açıkça yazılır:** scope-tabanlı bellek, *yalnızca* no-pointer /
|
||||||
|
value-semantics seçimi sayesinde geçerlidir. `interface` değerleri veya kaçan
|
||||||
|
referanslar eklenirse bu sorun **yeniden açılır**. (Bu, `interface`'i ertelemenin
|
||||||
|
ikinci sebebidir — bkz. ADR-018, ADR-019.)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ADR-015: Çalıştırma Modeli — IR + Bytecode VM (Makine-Kodu JIT Kapsam Dışı)
|
||||||
|
|
||||||
|
### Bağlam
|
||||||
|
|
||||||
|
Daha önceki belge/konuşmalarda çalıştırma için "JIT" terimi geçiyordu. Hangi
|
||||||
|
çalıştırma modeli? Üç uç var: tree-walker, bytecode VM, gerçek makine-kodu JIT.
|
||||||
|
|
||||||
|
### Değerlendirilen Yaklaşımlar
|
||||||
|
|
||||||
|
- **Tree-walker (AST'yi doğrudan gez-çalıştır):** en basit, ama **çok yavaş**;
|
||||||
|
her çalıştırmada ağaç gezilir.
|
||||||
|
- **Makine-kodu JIT** (register allocation, ABI/çağırma sözleşmeleri,
|
||||||
|
çalıştırılabilir `mmap` bellek): en hızlı; ama **tek faydası ham hızdır**, ki
|
||||||
|
burada öncelik değil. Determinizmi ve incelenebilirliği zorlaştırır, devasa
|
||||||
|
mühendislik yükü getirir.
|
||||||
|
- **IR + bytecode VM** (kendi IR'imize derle, yorumlayıcı döngü ile çalıştır):
|
||||||
|
determinizm ve incelenebilirliği **doğrudan** sağlar; tree-walker'dan hızlı;
|
||||||
|
bellek host heap'iyle kolay.
|
||||||
|
|
||||||
|
### Karar
|
||||||
|
|
||||||
|
✅ **IR + bytecode VM.** saQut kendi IR'sine derler ve bir yorumlayıcı döngüyle
|
||||||
|
çalıştırır.
|
||||||
|
|
||||||
|
- ❌ **Makine-kodu JIT kapsam dışıdır** (terminoloji düzeltmesi: "JIT" demeyi
|
||||||
|
bırak). Öncelikler **determinizm + incelenebilirlik** (toolbox), ham hız değil.
|
||||||
|
- **Bellek kolaydır:** host (C++) heap'i; özel runtime allocator yok (v0).
|
||||||
|
- **C'ye transpile, geçerli bir İKİNCİ backend olarak ileride kalır** (frontend
|
||||||
|
backend-bağımsız, ADR-006).
|
||||||
|
- İleride makine kodu **gerçekten** istenirse: elle code generator yazmak yerine
|
||||||
|
**libgccjit / LLVM'e bağlan** (ADR-001'deki QBE/custom değerlendirmeleri o gün
|
||||||
|
için geçerli). Bu **çok uzak gelecektir.**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ADR-016: FFI Seam — Host Fonksiyon Çağırma Deliği
|
||||||
|
|
||||||
|
### Bağlam
|
||||||
|
|
||||||
|
`print` bile bir "dış dünya" çağrısıdır; VM tek başına ekrana yazamaz, host'tan
|
||||||
|
bir fonksiyon çağırmalıdır. Bu ihtiyaç ya **kaza eseri** tek bir özel-durum
|
||||||
|
olarak gömülür, ya da **kasıtlı bir mekanizma** olarak tasarlanır.
|
||||||
|
|
||||||
|
### Karar
|
||||||
|
|
||||||
|
✅ **IR/runtime tasarımına bilinçli bir FFI seam konur:** "host fonksiyonu çağır"
|
||||||
|
için tek, genel bir IR mekanizması (örn. `callhost <id>, args...`).
|
||||||
|
|
||||||
|
- `print` bu seam'in **ilk müşterisidir**, özel-durum değil.
|
||||||
|
- İleride tüm "batteries" (bkz. ADR-017) bu sınır üzerinden gelir: sıkıştırma/
|
||||||
|
kripto C kütüphaneleri buraya bağlanır.
|
||||||
|
- **Neden şimdi:** seam'i sonradan eklemek IR ve VM'i baştan değiştirmeyi
|
||||||
|
gerektirir; deliği bir kez doğru açmak ucuzdur. Mekanizmayı **şimdi** doğru
|
||||||
|
tasarla, içini sonra doldur.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ADR-017: Batteries / Stdlib — Sınır Problemi (Ertelendi)
|
||||||
|
|
||||||
|
### Bağlam
|
||||||
|
|
||||||
|
Gerçek bir genel sürüm pil ile gelmeli (sıralama, sıkıştırma, kripto,
|
||||||
|
JSON/XML/HTML, ileride runtime/donanım, ses/görüntü/video). JSON/string
|
||||||
|
ergonomisi olmayan bir dil benimsenmez — bu doğru. Ama korku: pilleri çekirdeğe
|
||||||
|
gömmek **monolit** yaratır.
|
||||||
|
|
||||||
|
### Karar
|
||||||
|
|
||||||
|
✅ **Pil = sınır (boundary) problemi, "zlib'i yeniden yaz" problemi değil.**
|
||||||
|
|
||||||
|
- Çekirdek: **küçük bir gerçek builtin kümesi** (`print`, temel zorunlular) +
|
||||||
|
**gerisi kütüphane/FFI.**
|
||||||
|
- **JSON/XML/HTML ayrıştırıcıları saQut'ta yazılabilir** (string + struct +
|
||||||
|
fonksiyon + kontrol akışı yeter) — ilk "gerçek program" demoları.
|
||||||
|
- **Sıkıştırma/kripto:** denenmiş C kütüphanelerine **FFI** ile bağlan. **Kripto
|
||||||
|
asla elle yazılmaz.**
|
||||||
|
- **Bugüne tek yansıması:** FFI seam'i (ADR-016) bırak. Gerisi **v0 kapsamı
|
||||||
|
dışıdır.** Sınır bir kez çizilir, piller üstünde sonsuza dek birikir.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ADR-018: `interface` Ertelemesi (Reddedilmedi)
|
||||||
|
|
||||||
|
### Bağlam
|
||||||
|
|
||||||
|
Kullanıcı struct'ın yanında `interface` de istedi (crypto, compression, custom
|
||||||
|
data types, JSON, string için). `interface` alınmalı mı, ne zaman?
|
||||||
|
|
||||||
|
### Karar
|
||||||
|
|
||||||
|
✅ **Şimdi `struct`, `interface` ise ertelenir (reddedilmez).**
|
||||||
|
|
||||||
|
**Neden ertelendi:** `interface`, `struct`'tan kategorik olarak ağırdır.
|
||||||
|
- Struct yalnızca alan yerleşimidir (field layout).
|
||||||
|
- Interface "bu metotları sağlayan herhangi bir tip" demektir → çağrı yerinde
|
||||||
|
somut tip **bilinmez** → **dinamik dispatch** → vtable / fat pointer (içsel
|
||||||
|
pointer, izinli) → ve bir interface değeri **herhangi bir tipi tutabilir**, bu
|
||||||
|
da **kaçma/yaşam-süresi (escape/lifetime) problemini yeniden açar** (ADR-019).
|
||||||
|
- Go bunu fat pointer + GC ile çözer; saQut **GC istemiyor.**
|
||||||
|
|
||||||
|
Kullanıcının saydığı her şey (crypto, compression, custom data, JSON, string)
|
||||||
|
**yalnızca struct + fonksiyonla** yapılabilir (C bunu kanıtlar). Dolayısıyla:
|
||||||
|
şimdi struct'ı al, interface'i ertele.
|
||||||
|
|
||||||
|
**Metot-çağrı şekeri** (`list.push(5)` → `push(list, 5)`) ileride **parser
|
||||||
|
seviyesinde, sıfır semantik maliyetli** bir desugaring olarak eklenebilir —
|
||||||
|
şimdi değil.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ADR-019: Frontend ↔ Runtime Sorumluluk Ayrımı
|
||||||
|
|
||||||
|
### Bağlam
|
||||||
|
|
||||||
|
"Hangi CPU çekirdeği, hangi cihaz, ne zaman tetiklenir, hangi çıktı formatı"
|
||||||
|
gibi sorular nereye ait? Frontend'e mi, runtime'a mı?
|
||||||
|
|
||||||
|
### Karar
|
||||||
|
|
||||||
|
✅ **Net ayrım, frontend'i runtime kaygılarıyla yükleme:**
|
||||||
|
|
||||||
|
- **Frontend:** **yapı ve anlam** — tip, scope, dataflow. (Bu yol haritasının
|
||||||
|
konusu.)
|
||||||
|
- **Runtime/backend:** çekirdek/cihaz/tetikleme/çıktı formatı.
|
||||||
|
|
||||||
|
**Neden:** bu ayrım, kullanıcının önem verdiği **modülerliği korur**; frontend
|
||||||
|
backend-bağımsız kalır (ADR-006), böylece IR+VM ve ileride C-transpile aynı
|
||||||
|
frontend'den beslenir. Ayrıca **value-semantics + no-escape** kararının (kaçan
|
||||||
|
referans/closure yok) bağımlılığı buradadır: bu sayede scope-tabanlı bellek
|
||||||
|
çalışır (ADR-014). Closure veya interface değerleri eklemek bu ayrımı ve bellek
|
||||||
|
modelini birlikte zorlar — ikisi de bu yüzden ertelendi.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 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; `clone()` merkezi, sembol tablosu remap edilir |
|
||||||
|
| 008 | Optimizasyon konumu | Basitler AST'de, dataflow gerektirenler IR'de |
|
||||||
|
| 009 | Pass yönetimi | Fixpoint döngüsü, toggle'lı; monotonluk/iterasyon-tavanı değişmezi; akışa-bağlı analiz tur başına tazelenir |
|
||||||
|
| 010 | Tip sistemi | Minimal+genişletilebilir Type; gizli dönüşüm yok; Error tipi; tamsayı literali bağlama-göre tiplenir |
|
||||||
|
| 011 | Scope/forward ref | Global'de forward ref (fonksiyon/struct), ama global başlatıcı declare-before-use; lokal declare-before-use; döngüsel struct → `E010` |
|
||||||
|
| 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/closure yok; struct+array+tipli fonksiyon var; scope-tabanlı bellek gerekçeli |
|
||||||
|
| 015 | Çalıştırma modeli | IR + bytecode VM; makine-kodu JIT kapsam dışı |
|
||||||
|
| 016 | FFI seam | Kasıtlı "host fonksiyonu çağır" mekanizması; `print` ilk müşteri |
|
||||||
|
| 017 | Batteries/stdlib | Sınır problemi; küçük builtin + FFI/kütüphane; ertelendi |
|
||||||
|
| 018 | `interface` | Ertelendi (reddedilmedi); struct+fonksiyon yeter |
|
||||||
|
| 019 | Frontend↔runtime | Frontend yapı+anlam; çekirdek/cihaz/çıktı runtime'a ait |
|
||||||
|
|
@ -4,6 +4,23 @@
|
||||||
> gelecek yol haritası hakkında kapsamlı analizleri içerir.
|
> gelecek yol haritası hakkında kapsamlı analizleri içerir.
|
||||||
> Her kararın **neden** alındığı, alternatiflerin neden elendiği ve
|
> Her kararın **neden** alındığı, alternatiflerin neden elendiği ve
|
||||||
> gelecekte hangi koşullarda tekrar değerlendirileceği belirtilmiştir.
|
> gelecekte hangi koşullarda tekrar değerlendirileceği belirtilmiştir.
|
||||||
|
>
|
||||||
|
> ⚠️ **Hizalama notu (sonraki oturum):** Aşağıdaki ADR-001 ve ADR-005, backend
|
||||||
|
> kod-üretimi seçeneklerini (LLVM/QBE/custom/C-transpile) ve "JIT" terimini
|
||||||
|
> tartışır. Sonradan **çalıştırma modeli netleşti** ve bazı ifadeler güncellendi:
|
||||||
|
>
|
||||||
|
> - **Birincil çalıştırma modeli = IR + bytecode VM** (yorumlayıcı döngü).
|
||||||
|
> Bkz. `docs/adr-frontend-analiz.md` **ADR-015**.
|
||||||
|
> - **Gerçek makine-kodu JIT kapsam DIŞIDIR** (tek faydası ham hız; öncelik
|
||||||
|
> determinizm + incelenebilirlik). Buradaki "JIT" geçişleri bu ışıkta okunmalı.
|
||||||
|
> - **C-transpile**, ileride geçerli bir **ikinci backend**'tir; QBE/custom/LLVM
|
||||||
|
> ise "makine kodu gerçekten gerekirse libgccjit/LLVM'e bağlan" çerçevesinde
|
||||||
|
> **çok uzak gelecektir**. ADR-001'deki karşılaştırmalar o gün için geçerli.
|
||||||
|
> - "HeavyIR/LightIR" ikiliği (ADR-005) bir **gelecek fikri** olarak durur; v0'ın
|
||||||
|
> IR+VM hedefi tek, basit bir IR'dir + **FFI seam** (ADR-016).
|
||||||
|
> - **Yapılan vs planlanan:** ADR-001'deki "mevcut durum" listesi hâlâ doğrudur
|
||||||
|
> (lexer/tokenizer/parser/AST + minimal IR deneyi çalışır); kod üretimi ve VM
|
||||||
|
> **henüz yoktur.**
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
@ -0,0 +1,880 @@
|
||||||
|
# Complete saQut Compiler Issue List (English)
|
||||||
|
|
||||||
|
Copy each issue's title and body directly into Gitea.
|
||||||
|
Issues are ordered by stage, from most urgent to long-term.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Aşama 0: Metadata and Location Tracking
|
||||||
|
|
||||||
|
### Issue 0.1 — SourceFile and SourceLocation implementation
|
||||||
|
|
||||||
|
**Title:** Aşama 0.1 — Implement SourceFile and SourceLocation classes
|
||||||
|
|
||||||
|
**Body:**
|
||||||
|
|
||||||
|
**Goal:** Create the foundational metadata system so every token and AST node knows its exact origin (file, line, column, offset).
|
||||||
|
|
||||||
|
**Files to create/modify:**
|
||||||
|
- `src/core/location.hpp` — new file
|
||||||
|
- `src/core/sourcefile.hpp` — new file
|
||||||
|
|
||||||
|
**Requirements:**
|
||||||
|
- `SourceLocation` struct with fields: `filePath` (string), `line` (int), `column` (int), `offset` (int).
|
||||||
|
- `SourceFile` class that stores the full source text and a precomputed vector of line-start offsets. Provides `offsetToLocation(int offset) -> SourceLocation` using binary search (O(log n)).
|
||||||
|
- `SourceFile` constructor takes file path and source text; computes line offsets in one pass (O(n)).
|
||||||
|
- Both classes live in the `src/core/` directory.
|
||||||
|
|
||||||
|
**Success criteria:**
|
||||||
|
- Given a source string and offset, `offsetToLocation` returns correct line and column.
|
||||||
|
- Binary search is used, not linear scan.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Issue 0.2 — Add location tracking to Lexer
|
||||||
|
|
||||||
|
**Title:** Aşama 0.2 — Add line/column tracking to Lexer
|
||||||
|
|
||||||
|
**Body:**
|
||||||
|
|
||||||
|
**Goal:** Lexer updates current line and column on every `nextChar()` call.
|
||||||
|
|
||||||
|
**Files to modify:**
|
||||||
|
- `src/lexer/lexer.hpp`
|
||||||
|
|
||||||
|
**Requirements:**
|
||||||
|
- Add `int currentLine`, `int currentColumn` private fields to `Lexer`.
|
||||||
|
- On `nextChar()`: if character is `\n`, increment line and reset column; otherwise increment column.
|
||||||
|
- Add `SourceLocation getLocation()` method returning current position.
|
||||||
|
- Initialize line=1, column=1 in `setText()`.
|
||||||
|
- Modify `INumber` struct to include `SourceLocation startLoc` and `SourceLocation endLoc` (or keep start/end offsets and add location separately — prefer using `SourceLocation` fields).
|
||||||
|
|
||||||
|
**Success criteria:**
|
||||||
|
- After lexing any source, calling `getLocation()` returns correct line and column.
|
||||||
|
- `INumber` carries source location info.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Issue 0.3 — Add SourceLocation to Token base class
|
||||||
|
|
||||||
|
**Title:** Aşama 0.3 — Add SourceLocation to all Token types
|
||||||
|
|
||||||
|
**Body:**
|
||||||
|
|
||||||
|
**Goal:** Every token produced by the Tokenizer carries its SourceLocation.
|
||||||
|
|
||||||
|
**Files to modify:**
|
||||||
|
- `src/tokenizer/token.hpp`
|
||||||
|
|
||||||
|
**Requirements:**
|
||||||
|
- Add `SourceLocation loc` field to the base `Token` class.
|
||||||
|
- Remove or deprecate `int start, int end` fields (replace with `loc` data).
|
||||||
|
- Update `StringToken`, `NumberToken`, `IdentifierToken` if they reference start/end directly.
|
||||||
|
- Ensure all token constructors initialize `loc`.
|
||||||
|
|
||||||
|
**Success criteria:**
|
||||||
|
- After tokenizing, every token has a valid SourceLocation.
|
||||||
|
- Old start/end offsets are derivable from SourceLocation if needed (but location is primary).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Issue 0.4 — Add SourceLocation to ASTNode base class
|
||||||
|
|
||||||
|
**Title:** Aşama 0.4 — Add SourceLocation to all AST nodes
|
||||||
|
|
||||||
|
**Body:**
|
||||||
|
|
||||||
|
**Goal:** Every AST node knows its originating source location.
|
||||||
|
|
||||||
|
**Files to modify:**
|
||||||
|
- `src/parser/ast.hpp`
|
||||||
|
|
||||||
|
**Requirements:**
|
||||||
|
- Add `SourceLocation loc` field to `ASTNode` base class.
|
||||||
|
- Optionally add `SourceLocation endLoc` for range.
|
||||||
|
- Parser must set `loc` when creating AST nodes from tokens.
|
||||||
|
- Update `toJson()` and `log()` methods to include location info.
|
||||||
|
|
||||||
|
**Success criteria:**
|
||||||
|
- JSON output includes `"location": {"file": "...", "line": N, "column": M}` for every node.
|
||||||
|
- Log output shows location when available.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Aşama 1: CLI and REPL Mode
|
||||||
|
|
||||||
|
### Issue 1.1 — Implement REPL mode with readline support
|
||||||
|
|
||||||
|
**Title:** Aşama 1.1 — Implement REPL mode (`saqut` without arguments)
|
||||||
|
|
||||||
|
**Body:**
|
||||||
|
|
||||||
|
**Goal:** Running `saqut` without arguments enters an interactive REPL loop.
|
||||||
|
|
||||||
|
**Files to create/modify:**
|
||||||
|
- `src/cli/repl.hpp` — new file
|
||||||
|
- `src/main.cpp` — modify to detect no-args and launch REPL
|
||||||
|
|
||||||
|
**Requirements:**
|
||||||
|
- REPL prompt: `> `
|
||||||
|
- Each line is parsed, evaluated, and the result printed.
|
||||||
|
- `.ast` command prints the AST of the last expression.
|
||||||
|
- `.tokens` command prints the token list of the last expression.
|
||||||
|
- `.symbols` command prints the current symbol table.
|
||||||
|
- `.exit` or `.quit` exits the REPL.
|
||||||
|
- Multi-line input support: when a block is started (`{`), keep reading until `}`.
|
||||||
|
|
||||||
|
**Success criteria:**
|
||||||
|
- `./saqut` launches REPL.
|
||||||
|
- `.ast`, `.tokens`, `.symbols` work correctly.
|
||||||
|
- Multi-line input accumulates until balanced braces.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Issue 1.2 — Implement stdin mode (`saqut -`)
|
||||||
|
|
||||||
|
**Title:** Aşama 1.2 — Implement stdin reading mode
|
||||||
|
|
||||||
|
**Body:**
|
||||||
|
|
||||||
|
**Goal:** `saqut -` reads source code from standard input.
|
||||||
|
|
||||||
|
**Files to modify:**
|
||||||
|
- `src/cli/args.hpp`
|
||||||
|
|
||||||
|
**Requirements:**
|
||||||
|
- When `-` is passed as positional argument, read all stdin until EOF.
|
||||||
|
- Works with all commands: `saqut run -`, `saqut tokens -`, etc.
|
||||||
|
- Remove the current "TODO" stub and implement fully.
|
||||||
|
|
||||||
|
**Success criteria:**
|
||||||
|
- `echo "int main() { return 42; }" | ./saqut run -` works.
|
||||||
|
- `cat file.sqt | ./saqut tokens -` works.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Issue 1.3 — Implement output file support for all commands
|
||||||
|
|
||||||
|
**Title:** Aşama 1.3 — Support `-o/--output` flag for all commands
|
||||||
|
|
||||||
|
**Body:**
|
||||||
|
|
||||||
|
**Goal:** All CLI commands respect `-o outputfile` to write results to a file instead of stdout.
|
||||||
|
|
||||||
|
**Files to modify:**
|
||||||
|
- `src/cli/commands/run.hpp`
|
||||||
|
- `src/cli/commands/tokens.hpp`
|
||||||
|
- `src/cli/commands/symbols.hpp`
|
||||||
|
|
||||||
|
**Requirements:**
|
||||||
|
- `cmdRun`, `cmdTokens`, `cmdSymbols` already partially support `-o`; ensure all do.
|
||||||
|
- If `-o` is provided, write output to the specified file; otherwise stdout.
|
||||||
|
- Handle file open errors gracefully.
|
||||||
|
|
||||||
|
**Success criteria:**
|
||||||
|
- `saqut tokens source.sqt -o tokens.txt` writes to file.
|
||||||
|
- `saqut symbols source.sqt --output=symbols.json` writes JSON to file.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Aşama 2: AST — Memory Monster
|
||||||
|
|
||||||
|
### Issue 2.1 — Migrate to unique_ptr for AST-owned tokens
|
||||||
|
|
||||||
|
**Title:** Aşama 2.1 — Use std::unique_ptr for token ownership in AST
|
||||||
|
|
||||||
|
**Body:**
|
||||||
|
|
||||||
|
**Goal:** AST nodes own their tokens via `std::unique_ptr`, eliminating memory leaks.
|
||||||
|
|
||||||
|
**Files to modify:**
|
||||||
|
- `src/parser/ast.hpp`
|
||||||
|
- `src/parser/parser.hpp`
|
||||||
|
- `src/parser/token.hpp`
|
||||||
|
|
||||||
|
**Requirements:**
|
||||||
|
- `ParserToken::token` changes from `Token*` to `std::unique_ptr<Token>`.
|
||||||
|
- All AST nodes that store a `ParserToken` or `Token*` must use `std::unique_ptr`.
|
||||||
|
- Parser transfers ownership when creating nodes.
|
||||||
|
- Remove manual `delete` calls on tokens (they are now owned by AST nodes).
|
||||||
|
|
||||||
|
**Success criteria:**
|
||||||
|
- No memory leaks when parsing and deleting AST.
|
||||||
|
- Valgrind/ASan reports zero leaks on test cases.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Issue 2.2 — Implement ASTNode::getSourceText()
|
||||||
|
|
||||||
|
**Title:** Aşama 2.2 — Add getSourceText() and getSourceRange() to ASTNode
|
||||||
|
|
||||||
|
**Body:**
|
||||||
|
|
||||||
|
**Goal:** Given any AST node, retrieve the exact source code substring it represents.
|
||||||
|
|
||||||
|
**Files to modify:**
|
||||||
|
- `src/parser/ast.hpp`
|
||||||
|
- `src/core/sourcefile.hpp`
|
||||||
|
|
||||||
|
**Requirements:**
|
||||||
|
- `ASTNode::getSourceText()` returns `std::string` — the original source code for this node.
|
||||||
|
- `ASTNode::getSourceRange()` returns `std::pair<SourceLocation, SourceLocation>` (start and end).
|
||||||
|
- Requires AST nodes to store a reference to the `SourceFile` (or the full source text).
|
||||||
|
- This powers future rich error messages with `^^^^` source highlighting.
|
||||||
|
|
||||||
|
**Success criteria:**
|
||||||
|
- For a BinaryExpression node representing `a + b`, `getSourceText()` returns `"a + b"`.
|
||||||
|
- For an IfStatement, returns the entire `if (...) { ... }` block text.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Issue 2.3 — Implement Graphviz DOT format output
|
||||||
|
|
||||||
|
**Title:** Aşama 2.3 — Add --format=dot for AST visualization
|
||||||
|
|
||||||
|
**Body:**
|
||||||
|
|
||||||
|
**Goal:** Export the AST as a Graphviz DOT file for graphical visualization.
|
||||||
|
|
||||||
|
**Files to create/modify:**
|
||||||
|
- `src/format/dot.hpp` — new file
|
||||||
|
- `src/cli/commands/ast.hpp` — modify to support `--format=dot`
|
||||||
|
|
||||||
|
**Requirements:**
|
||||||
|
- Implement `astToDot(ASTNode*) -> std::string` function.
|
||||||
|
- Each node becomes a labeled box.
|
||||||
|
- Parent-child relationships become directed edges.
|
||||||
|
- Node labels show kind and name (e.g., `BinaryExpression +`).
|
||||||
|
- Output is valid DOT format, renderable by `dot -Tpng -o ast.png ast.dot`.
|
||||||
|
|
||||||
|
**Success criteria:**
|
||||||
|
- `saqut ast source.sqt --format=dot -o ast.dot` produces a valid DOT file.
|
||||||
|
- The resulting image shows a readable tree.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Aşama 3: Symbol Table
|
||||||
|
|
||||||
|
### Issue 3.1 — Implement Symbol and SymbolTable classes
|
||||||
|
|
||||||
|
**Title:** Aşama 3.1 — Implement Symbol struct and SymbolTable class with nested scopes
|
||||||
|
|
||||||
|
**Body:**
|
||||||
|
|
||||||
|
**Goal:** Build a full symbol table with nested scope support.
|
||||||
|
|
||||||
|
**Files to create:**
|
||||||
|
- `src/symbol/symbol.hpp` — new file
|
||||||
|
- `src/symbol/symbol_table.hpp` — new file
|
||||||
|
|
||||||
|
**Requirements:**
|
||||||
|
|
||||||
|
`Symbol` struct:
|
||||||
|
- `name` (string)
|
||||||
|
- `kind` (enum: Variable, Function, Parameter, Type, Struct)
|
||||||
|
- `type` (Type* or string for now)
|
||||||
|
- `definitionLoc` (SourceLocation)
|
||||||
|
- `references` (vector of SourceLocation)
|
||||||
|
- `scope` (pointer to parent scope or scope level)
|
||||||
|
- `metadata` (optional map<string,string>)
|
||||||
|
|
||||||
|
`SymbolTable` class:
|
||||||
|
- Nested scope stack: `enterScope()`, `exitScope()`.
|
||||||
|
- `define(Symbol) -> bool` (returns false on duplicate in same scope).
|
||||||
|
- `resolve(name) -> Symbol*` (searches innermost to outermost).
|
||||||
|
- `addReference(name, location)` (appends to symbol's reference list).
|
||||||
|
- `getAllSymbols() -> vector<Symbol*>` (flat list of all symbols in all scopes).
|
||||||
|
- `toJson() -> string` for serialization.
|
||||||
|
|
||||||
|
**Success criteria:**
|
||||||
|
- Nested scopes work: variable in inner scope shadows outer.
|
||||||
|
- Duplicate definition in same scope returns false.
|
||||||
|
- resolve finds symbols across scope boundaries.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Issue 3.2 — Implement SymbolCollector AST walker
|
||||||
|
|
||||||
|
**Title:** Aşama 3.2 — Implement SymbolCollector that populates SymbolTable from AST
|
||||||
|
|
||||||
|
**Body:**
|
||||||
|
|
||||||
|
**Goal:** Walk the AST and populate the SymbolTable with all definitions and references.
|
||||||
|
|
||||||
|
**Files to create/modify:**
|
||||||
|
- `src/symbol/symbol_collector.hpp` — new file
|
||||||
|
- Replace or refactor the simple `collectSymbolsRecursive` in `src/json.hpp`
|
||||||
|
|
||||||
|
**Requirements:**
|
||||||
|
- `SymbolCollector` class with method `collect(ASTNode* root, SymbolTable* table)`.
|
||||||
|
- Walks all AST node types (Program, FunctionDecl, VariableDecl, Block, etc.).
|
||||||
|
- Calls `table->define()` for declarations.
|
||||||
|
- Calls `table->addReference()` for identifier usages.
|
||||||
|
- Handles all AST node types currently in `ast.hpp` (19 types).
|
||||||
|
- Replaces the ad-hoc `SymbolEntry` vector with proper SymbolTable population.
|
||||||
|
|
||||||
|
**Success criteria:**
|
||||||
|
- After parsing `source.sqt`, SymbolTable contains all functions and variables.
|
||||||
|
- References are collected for each symbol.
|
||||||
|
- `saqut symbols source.sqt` shows the enriched data.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Issue 3.3 — Semantic error: undefined variable
|
||||||
|
|
||||||
|
**Title:** Aşama 3.3 — Report "undefined variable" errors using SymbolTable
|
||||||
|
|
||||||
|
**Body:**
|
||||||
|
|
||||||
|
**Goal:** When a variable is used before definition, report a clear error with location.
|
||||||
|
|
||||||
|
**Files to modify:**
|
||||||
|
- `src/symbol/symbol_collector.hpp`
|
||||||
|
- `src/core/diagnostic.hpp` — new file (or extend existing error reporting)
|
||||||
|
|
||||||
|
**Requirements:**
|
||||||
|
- During symbol collection, when an identifier reference has no matching `resolve()`, emit a diagnostic.
|
||||||
|
- Diagnostic includes: error level, SourceLocation, message, optional hint.
|
||||||
|
- `"Variable 'x' is not defined. Did you mean 'xy'?"` if a close match exists (Levenshtein distance < 3).
|
||||||
|
- Diagnostic system supports multiple errors (don't stop at first).
|
||||||
|
|
||||||
|
**Success criteria:**
|
||||||
|
- `int main() { return x; }` reports: `Error: 'x' is not defined at line 1 column 19`.
|
||||||
|
- Typos suggest close matches.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Issue 3.4 — Semantic error: duplicate definition
|
||||||
|
|
||||||
|
**Title:** Aşama 3.4 — Report "duplicate definition" errors
|
||||||
|
|
||||||
|
**Body:**
|
||||||
|
|
||||||
|
**Goal:** When a symbol is defined twice in the same scope, report an error.
|
||||||
|
|
||||||
|
**Files to modify:**
|
||||||
|
- `src/symbol/symbol_table.hpp`
|
||||||
|
- `src/symbol/symbol_collector.hpp`
|
||||||
|
|
||||||
|
**Requirements:**
|
||||||
|
- `SymbolTable::define()` returns false on duplicate; collector emits diagnostic.
|
||||||
|
- Error message: `"Function 'main' is already defined. Previous definition at line X."`
|
||||||
|
- Works for variables, functions, structs.
|
||||||
|
|
||||||
|
**Success criteria:**
|
||||||
|
- Two `int main()` definitions produce an error.
|
||||||
|
- Two `int x` in the same block produce an error.
|
||||||
|
- Shadowing in nested scopes is allowed (not an error).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Aşama 4: Feature Toggle System
|
||||||
|
|
||||||
|
### Issue 4.1 — Implement CompilerConfig struct and flag parsing
|
||||||
|
|
||||||
|
**Title:** Aşama 4.1 — Implement CompilerConfig struct and --disable-* flags
|
||||||
|
|
||||||
|
**Body:**
|
||||||
|
|
||||||
|
**Goal:** Create a configuration system that controls language features at compile time.
|
||||||
|
|
||||||
|
**Files to create/modify:**
|
||||||
|
- `src/core/config.hpp` — new file
|
||||||
|
- `src/cli/args.hpp` — extend to parse feature flags
|
||||||
|
|
||||||
|
**Requirements:**
|
||||||
|
|
||||||
|
`CompilerConfig` struct with boolean fields:
|
||||||
|
- `enableWhile`, `enableFor`, `enableDoWhile`, `enableSwitch`
|
||||||
|
- `enableClass`, `enableInterface`, `enableEnum`
|
||||||
|
- `enableTernary`, `enablePostfix`, `enableUnary`
|
||||||
|
- `optConstantFolding`, `optDeadCodeElim`
|
||||||
|
- `outputFormat` (text/json/dot)
|
||||||
|
- `mode` (run/tokens/ast/symbols/compile/transpile)
|
||||||
|
|
||||||
|
CLI flags:
|
||||||
|
- `--disable-while` sets `enableWhile = false`
|
||||||
|
- `--disable-for` sets `enableFor = false`
|
||||||
|
- `--opt-all` enables all optimizations
|
||||||
|
- `--opt-none` disables all optimizations
|
||||||
|
|
||||||
|
**Success criteria:**
|
||||||
|
- `--disable-while` flag is parsed into CompilerConfig.
|
||||||
|
- Config is passed through to Tokenizer and Parser.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Issue 4.2 — Implement keyword toggling in Tokenizer
|
||||||
|
|
||||||
|
**Title:** Aşama 4.2 — Disable keywords based on CompilerConfig
|
||||||
|
|
||||||
|
**Body:**
|
||||||
|
|
||||||
|
**Goal:** When a keyword is disabled in config, the Tokenizer treats it as an identifier.
|
||||||
|
|
||||||
|
**Files to modify:**
|
||||||
|
- `src/tokenizer/tokenizer.hpp`
|
||||||
|
|
||||||
|
**Requirements:**
|
||||||
|
- Tokenizer receives a `CompilerConfig` reference (or copy).
|
||||||
|
- Before matching keywords, check config flags.
|
||||||
|
- Disabled keywords are skipped in keyword matching; they fall through to identifier.
|
||||||
|
- Example: `--disable-while` means `while` becomes a regular identifier.
|
||||||
|
|
||||||
|
**Success criteria:**
|
||||||
|
- With `--disable-while`, `while (true) {}` tokenizes `while` as identifier.
|
||||||
|
- Parser then does not parse it as a while statement (falls through to expression).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Issue 4.3 — Implement optimization pass interface
|
||||||
|
|
||||||
|
**Title:** Aşama 4.3 — Implement OptimizationPass interface and OptimizationManager
|
||||||
|
|
||||||
|
**Body:**
|
||||||
|
|
||||||
|
**Goal:** Create a framework for pluggable optimization passes.
|
||||||
|
|
||||||
|
**Files to create:**
|
||||||
|
- `src/opt/optimization_pass.hpp` — new file
|
||||||
|
- `src/opt/optimization_manager.hpp` — new file
|
||||||
|
|
||||||
|
**Requirements:**
|
||||||
|
- `OptimizationPass` abstract class with `run(ASTNode* root, SymbolTable* table) -> bool` method.
|
||||||
|
- `OptimizationManager` holds a list of passes, runs them in order based on CompilerConfig.
|
||||||
|
- Initially, two passes: `ConstantFoldingPass` and `DeadCodeEliminationPass` (empty implementations for now, will be filled in Aşama 6).
|
||||||
|
- `--skip-constant-folding` flag skips that pass.
|
||||||
|
|
||||||
|
**Success criteria:**
|
||||||
|
- OptimizationManager runs (even if passes are no-ops for now).
|
||||||
|
- Feature flags control which passes execute.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Aşama 5: Backend — Execution
|
||||||
|
|
||||||
|
### Issue 5.1 — Strengthen IR with control flow and function opcodes
|
||||||
|
|
||||||
|
**Title:** Aşama 5.1 — Extend IR with control flow, function, and memory opcodes
|
||||||
|
|
||||||
|
**Body:**
|
||||||
|
|
||||||
|
**Goal:** IR must support control flow (branch, jump, compare), function calls, and memory operations before any backend can work.
|
||||||
|
|
||||||
|
**Files to modify:**
|
||||||
|
- `src/ir/ir.hpp`
|
||||||
|
|
||||||
|
**Requirements:**
|
||||||
|
|
||||||
|
New opcodes:
|
||||||
|
- Control flow: `cmp`, `br`, `br_eq`, `br_lt`, `br_gt`, `jmp`
|
||||||
|
- Function: `call`, `ret`, `param`
|
||||||
|
- Memory: `load`, `store`, `alloca`
|
||||||
|
|
||||||
|
Update `IROpData` if needed to support new parameter types (labels for jump targets, function indices).
|
||||||
|
Add a `label` field or a separate `IRLabel` structure for branch targets.
|
||||||
|
|
||||||
|
**Success criteria:**
|
||||||
|
- All opcodes are defined and documented.
|
||||||
|
- IR can represent a simple if-else and a while loop.
|
||||||
|
- IR can represent a function definition with parameters and return.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Issue 5.2 — Implement C Transpile Backend
|
||||||
|
|
||||||
|
**Title:** Aşama 5.2 — Implement C transpile backend (`saqut transpile`)
|
||||||
|
|
||||||
|
**Body:**
|
||||||
|
|
||||||
|
**Goal:** Convert saQut IR (or AST) to compilable C source code.
|
||||||
|
|
||||||
|
**Files to create:**
|
||||||
|
- `src/backend/c_transpile.hpp` — new file
|
||||||
|
|
||||||
|
**Requirements:**
|
||||||
|
- Reads IR (or AST) and generates equivalent C code.
|
||||||
|
- Handles: variable declarations, binary expressions, if/for/while/do-while, function definitions, return.
|
||||||
|
- Generates readable C with reasonable indentation.
|
||||||
|
- Embed `#line` directives so GCC/Clang error messages point to original `.sqt` files.
|
||||||
|
- `saqut transpile source.sqt -o output.c` command.
|
||||||
|
|
||||||
|
**Success criteria:**
|
||||||
|
- Given `int main() { return 42; }`, generates compilable C code that returns 42.
|
||||||
|
- Generated C compiles with `gcc -Wall -Werror` without warnings.
|
||||||
|
- `saqut compile source.sqt output:prog` compiles and runs correctly.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Issue 5.3 — Implement Interpreter (Tree-walk VM)
|
||||||
|
|
||||||
|
**Title:** Aşama 5.3 — Implement interpreter VM (`saqut run` execution)
|
||||||
|
|
||||||
|
**Body:**
|
||||||
|
|
||||||
|
**Goal:** Execute saQut programs by walking the AST or interpreting IR directly.
|
||||||
|
|
||||||
|
**Files to create:**
|
||||||
|
- `src/backend/interpreter.hpp` — new file
|
||||||
|
|
||||||
|
**Requirements:**
|
||||||
|
- `Interpreter` class that walks AST and executes.
|
||||||
|
- Supports: variable declaration and assignment, binary/unary expressions, if/else, while/for/do-while, break/continue, return, function calls (after function parameters are implemented).
|
||||||
|
- Stack-based or register-based value storage.
|
||||||
|
- `saqut run source.sqt` executes and prints program result.
|
||||||
|
- REPL mode (from Issue 1.1) uses the interpreter for evaluation.
|
||||||
|
|
||||||
|
**Success criteria:**
|
||||||
|
- `saqut run source.sqt` executes correctly for arithmetic and control flow.
|
||||||
|
- Variables hold values across statements.
|
||||||
|
- Return value propagates correctly.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Aşama 6: Optimization
|
||||||
|
|
||||||
|
### Issue 6.1 — Implement Constant Folding pass
|
||||||
|
|
||||||
|
**Title:** Aşama 6.1 — Implement Constant Folding optimization
|
||||||
|
|
||||||
|
**Body:**
|
||||||
|
|
||||||
|
**Goal:** Evaluate constant expressions at compile time.
|
||||||
|
|
||||||
|
**Files to create:**
|
||||||
|
- `src/opt/constant_folding.hpp` — new file
|
||||||
|
|
||||||
|
**Requirements:**
|
||||||
|
- Walk AST, find `BinaryExpression` nodes where both operands are Literals.
|
||||||
|
- Compute the result, replace the subtree with a single Literal node.
|
||||||
|
- Handles: `+`, `-`, `*`, `/`, `%` for integers and floats.
|
||||||
|
- Handles unary `-` on literals.
|
||||||
|
- Guard against division by zero (emit warning, skip folding).
|
||||||
|
|
||||||
|
**Success criteria:**
|
||||||
|
- `4 + 5` becomes `9` in the optimized AST.
|
||||||
|
- `x + 0` is NOT folded (x is not constant).
|
||||||
|
- `1 / 0` emits warning, AST unchanged.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Issue 6.2 — Implement Dead Code Elimination pass
|
||||||
|
|
||||||
|
**Title:** Aşama 6.2 — Implement Dead Code Elimination
|
||||||
|
|
||||||
|
**Body:**
|
||||||
|
|
||||||
|
**Goal:** Remove code that is provably unreachable.
|
||||||
|
|
||||||
|
**Files to create:**
|
||||||
|
- `src/opt/dead_code_elim.hpp` — new file
|
||||||
|
|
||||||
|
**Requirements:**
|
||||||
|
- Remove statements after `return`, `break`, `continue` within the same block.
|
||||||
|
- Remove `if (false)` branches.
|
||||||
|
- Remove `while (false)` bodies.
|
||||||
|
- Remove unused variable declarations (requires SymbolTable reference counting).
|
||||||
|
|
||||||
|
**Success criteria:**
|
||||||
|
- `return; x = 5;` — assignment is removed.
|
||||||
|
- `if (false) { ... }` — entire block removed.
|
||||||
|
- Unused variable `int y = 10;` removed when y has zero references.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Issue 6.3 — Implement Null Check and Type Check Elimination
|
||||||
|
|
||||||
|
**Title:** Aşama 6.3 — Implement Null/Type Check Elimination
|
||||||
|
|
||||||
|
**Body:**
|
||||||
|
|
||||||
|
**Goal:** Remove redundant null checks and type checks when the compiler can prove they are unnecessary.
|
||||||
|
|
||||||
|
**Files to create:**
|
||||||
|
- `src/opt/null_check_elim.hpp` — new file
|
||||||
|
- `src/opt/type_check_elim.hpp` — new file
|
||||||
|
|
||||||
|
**Requirements:**
|
||||||
|
- Track which variables have been checked for null in the current path.
|
||||||
|
- If a variable was already null-checked (and not reassigned), skip subsequent checks.
|
||||||
|
- Similarly for type checks (`is` expressions).
|
||||||
|
- Requires dataflow analysis within a function body.
|
||||||
|
|
||||||
|
**Success criteria:**
|
||||||
|
- Two consecutive `if (x != null)` — second check eliminated.
|
||||||
|
- `if (x is int) { ... if (x is int) { ... } }` — inner check eliminated.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Aşama 7: Test and Performance
|
||||||
|
|
||||||
|
### Issue 7.1 — Set up unit test framework (Google Test)
|
||||||
|
|
||||||
|
**Title:** Aşama 7.1 — Set up Google Test framework and write initial tests
|
||||||
|
|
||||||
|
**Body:**
|
||||||
|
|
||||||
|
**Goal:** Create a proper testing infrastructure.
|
||||||
|
|
||||||
|
**Files to create/modify:**
|
||||||
|
- `tests/` directory
|
||||||
|
- `tests/lexer_test.cpp`
|
||||||
|
- `tests/tokenizer_test.cpp`
|
||||||
|
- `tests/parser_test.cpp`
|
||||||
|
- `tests/symbol_test.cpp`
|
||||||
|
- `CMakeLists.txt` or `Makefile` with test target
|
||||||
|
|
||||||
|
**Requirements:**
|
||||||
|
- Use Google Test (download during build or include as submodule).
|
||||||
|
- Initial tests: lexing numbers, tokenizing keywords, parsing simple expressions, symbol collection.
|
||||||
|
- `make test` or `cmake --build . --target test` runs all tests.
|
||||||
|
|
||||||
|
**Success criteria:**
|
||||||
|
- At least 10 passing tests.
|
||||||
|
- CI-ready: tests can be run from command line.
|
||||||
|
- Failures show expected vs actual.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Issue 7.2 — Write snapshot tests for AST output
|
||||||
|
|
||||||
|
**Title:** Aşama 7.2 — Snapshot testing for AST/IR/symbol output
|
||||||
|
|
||||||
|
**Body:**
|
||||||
|
|
||||||
|
**Goal:** Ensure compiler output is stable across changes.
|
||||||
|
|
||||||
|
**Files to create:**
|
||||||
|
- `tests/snapshots/` directory
|
||||||
|
- Script or C++ test that runs `saqut ast` and compares to stored JSON.
|
||||||
|
|
||||||
|
**Requirements:**
|
||||||
|
- Store known-good JSON output for `source.sqt`, `Final.sqt`.
|
||||||
|
- Test compares current output to snapshot; fails on difference.
|
||||||
|
- Snapshot update mode to regenerate expected files.
|
||||||
|
|
||||||
|
**Success criteria:**
|
||||||
|
- Changes to parser that affect AST structure are caught.
|
||||||
|
- False positives (formatting changes) are manageable.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Issue 7.3 — Implement benchmark suite
|
||||||
|
|
||||||
|
**Title:** Aşama 7.3 — Implement benchmark infrastructure (`saqut bench`)
|
||||||
|
|
||||||
|
**Body:**
|
||||||
|
|
||||||
|
**Goal:** Measure compiler performance on large inputs.
|
||||||
|
|
||||||
|
**Files to create/modify:**
|
||||||
|
- `src/cli/commands/bench.hpp` — new file
|
||||||
|
- `benchmarks/` directory with test files
|
||||||
|
|
||||||
|
**Requirements:**
|
||||||
|
- `saqut bench` runs a set of benchmark files and reports parse time, token throughput, memory usage.
|
||||||
|
- Warm-up phase to reduce noise.
|
||||||
|
- Output in machine-readable format (JSON) for tracking over time.
|
||||||
|
|
||||||
|
**Success criteria:**
|
||||||
|
- Parse a 10K-line file and report tokens/second.
|
||||||
|
- Memory usage reported in KB/MB.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Aşama 8: Advanced Type System
|
||||||
|
|
||||||
|
### Issue 8.1 — Implement Struct type (user-defined types)
|
||||||
|
|
||||||
|
**Title:** Aşama 8.1 — Full struct support: definition, instantiation, field access
|
||||||
|
|
||||||
|
**Body:**
|
||||||
|
|
||||||
|
**Goal:** Users can define and use `struct` types.
|
||||||
|
|
||||||
|
**Files to modify:**
|
||||||
|
- `src/parser/parser.hpp`
|
||||||
|
- `src/parser/ast.hpp`
|
||||||
|
- `src/symbol/`
|
||||||
|
- `src/backend/`
|
||||||
|
|
||||||
|
**Requirements:**
|
||||||
|
- `struct Point { int x; int y; }` defines a type.
|
||||||
|
- `Point p;` declares a variable of that type.
|
||||||
|
- `p.x` accesses a field (already partially supported via MemberAccess).
|
||||||
|
- Struct type checking: field must exist, type must match on assignment.
|
||||||
|
- C transpile and interpreter both support structs.
|
||||||
|
|
||||||
|
**Success criteria:**
|
||||||
|
- Struct definition, instantiation, and field access work end-to-end.
|
||||||
|
- Accessing nonexistent field produces clear error.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Issue 8.2 — Implement Array and Pointer types
|
||||||
|
|
||||||
|
**Title:** Aşama 8.2 — Implement array and pointer type support
|
||||||
|
|
||||||
|
**Body:**
|
||||||
|
|
||||||
|
**Goal:** Support `int[]`, `int*`, array indexing, and pointer arithmetic.
|
||||||
|
|
||||||
|
**Files to modify:**
|
||||||
|
- `src/parser/parser.hpp`
|
||||||
|
- `src/parser/ast.hpp`
|
||||||
|
- `src/symbol/`
|
||||||
|
- `src/backend/`
|
||||||
|
|
||||||
|
**Requirements:**
|
||||||
|
- `int arr[10];` array declaration.
|
||||||
|
- `arr[i]` indexing (already partially supported).
|
||||||
|
- `int* p;` pointer declaration.
|
||||||
|
- `*p` dereference (unary `*` operator).
|
||||||
|
- `&x` address-of operator.
|
||||||
|
- Type checking for pointer/array operations.
|
||||||
|
|
||||||
|
**Success criteria:**
|
||||||
|
- Array declaration and indexing work.
|
||||||
|
- Pointer declaration, assignment, dereference work.
|
||||||
|
- Pointer arithmetic (`p + 1`) works.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Issue 8.3 — Implement standard library foundation (lib/std.sqt)
|
||||||
|
|
||||||
|
**Title:** Aşama 8.3 — Create standard library with basic data structures
|
||||||
|
|
||||||
|
**Body:**
|
||||||
|
|
||||||
|
**Goal:** Provide built-in data structures: List, Map, Set, Buffer, String utilities.
|
||||||
|
|
||||||
|
**Files to create:**
|
||||||
|
- `lib/std.sqt` — standard library source file
|
||||||
|
- `lib/collections.sqt`, `lib/io.sqt`, `lib/encoding.sqt` — optional modules
|
||||||
|
|
||||||
|
**Requirements:**
|
||||||
|
- `List<T>` — dynamic array with `add`, `get`, `remove`, `size`.
|
||||||
|
- `Map<K,V>` — hash map with `put`, `get`, `contains`, `remove`.
|
||||||
|
- `Set<T>` — hash set.
|
||||||
|
- `Buffer` — byte buffer for binary data.
|
||||||
|
- `String` methods: `split`, `replace`, `substring`, `toUpper`, `toLower`.
|
||||||
|
- All implemented in saQut itself (or native functions exposed to saQut).
|
||||||
|
|
||||||
|
**Success criteria:**
|
||||||
|
- `import std;` makes these types available.
|
||||||
|
- Basic operations work without crashes.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Aşama 9: Ecosystem
|
||||||
|
|
||||||
|
### Issue 9.1 — Implement project initialization (`saqut init`)
|
||||||
|
|
||||||
|
**Title:** Aşama 9.1 — Implement `saqut init` project scaffolding
|
||||||
|
|
||||||
|
**Body:**
|
||||||
|
|
||||||
|
**Goal:** `saqut init my-project` creates a standard project directory.
|
||||||
|
|
||||||
|
**Files to create/modify:**
|
||||||
|
- `src/cli/commands/init.hpp` — new file
|
||||||
|
|
||||||
|
**Requirements:**
|
||||||
|
- Creates directory with:
|
||||||
|
- `project.saqut` manifest file (TOML or JSON format).
|
||||||
|
- `src/` directory with `main.sqt`.
|
||||||
|
- `.gitignore` file.
|
||||||
|
- Manifest contains: project name, version, description, author, dependencies (empty initially).
|
||||||
|
|
||||||
|
**Success criteria:**
|
||||||
|
- `saqut init testproj` creates the expected structure.
|
||||||
|
- `saqut run` inside the project directory finds and runs `src/main.sqt`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Issue 9.2 — Implement built-in test framework (`saqut test`)
|
||||||
|
|
||||||
|
**Title:** Aşama 9.2 — Implement built-in test framework
|
||||||
|
|
||||||
|
**Body:**
|
||||||
|
|
||||||
|
**Goal:** `saqut test` discovers and runs test functions in the project.
|
||||||
|
|
||||||
|
**Files to create/modify:**
|
||||||
|
- `src/cli/commands/test.hpp` — new file
|
||||||
|
- `lib/test.sqt` — test framework library
|
||||||
|
|
||||||
|
**Requirements:**
|
||||||
|
- Functions annotated with `#[test]` or named `test_*` are test functions.
|
||||||
|
- `saqut test` runs all tests, reports pass/fail.
|
||||||
|
- `assert(condition)` and `assert_eq(a, b)` built-in functions.
|
||||||
|
- Output in TAP or JUnit XML format for CI integration.
|
||||||
|
|
||||||
|
**Success criteria:**
|
||||||
|
- `saqut test` in a project with test functions runs them.
|
||||||
|
- Failures report file and line number.
|
||||||
|
- Exit code 0 for all pass, non-zero for any failure.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Aşama 10: Package Manager
|
||||||
|
|
||||||
|
### Issue 10.1 — Implement package manager foundation (`saqut add`)
|
||||||
|
|
||||||
|
**Title:** Aşama 10.1 — Implement `saqut add` package manager
|
||||||
|
|
||||||
|
**Body:**
|
||||||
|
|
||||||
|
**Goal:** `saqut add <package>` downloads and installs a package dependency.
|
||||||
|
|
||||||
|
**Files to create/modify:**
|
||||||
|
- `src/cli/commands/add.hpp` — new file
|
||||||
|
- `src/package/registry.hpp` — new file
|
||||||
|
- `src/package/resolver.hpp` — new file
|
||||||
|
|
||||||
|
**Requirements:**
|
||||||
|
- Package registry: a central Git repository or simple HTTP server listing available packages.
|
||||||
|
- `saqut add json` adds the `json` package to `project.saqut` dependencies.
|
||||||
|
- Downloads package source into `packages/` directory (or a cache).
|
||||||
|
- `import json;` in source code finds the installed package.
|
||||||
|
- Semantic versioning support (major.minor.patch).
|
||||||
|
|
||||||
|
**Success criteria:**
|
||||||
|
- `saqut add` adds dependency to manifest.
|
||||||
|
- `import` of installed package works.
|
||||||
|
- Version constraints are enforced.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Aşama 11: Language Specification
|
||||||
|
|
||||||
|
### Issue 11.1 — Write language specification document
|
||||||
|
|
||||||
|
**Title:** Aşama 11.1 — Write comprehensive language specification
|
||||||
|
|
||||||
|
**Body:**
|
||||||
|
|
||||||
|
**Goal:** Create `docs/lang_spec.md` that fully defines the saQut language.
|
||||||
|
|
||||||
|
**Files to create:**
|
||||||
|
- `docs/lang_spec.md`
|
||||||
|
|
||||||
|
**Requirements:**
|
||||||
|
- Syntax: full grammar in EBNF or similar notation.
|
||||||
|
- Type system: all built-in types, conversion rules, type inference.
|
||||||
|
- Control flow: semantics of if/else, for, while, do-while, break, continue, return.
|
||||||
|
- Memory model: stack vs heap, pointer rules, array layout.
|
||||||
|
- Standard library: function signatures and contracts for all `lib/std.sqt` functions.
|
||||||
|
- Error handling: exception-like or error-return semantics.
|
||||||
|
|
||||||
|
**Success criteria:**
|
||||||
|
- A developer can implement a saQut compiler from only this document.
|
||||||
|
- All implemented features are documented.
|
||||||
|
- Unimplemented features are clearly marked as "planned" or "future."
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
> **Total issues: 30**
|
||||||
|
>
|
||||||
|
> **Order:** Start from 0.1 and work sequentially. Each issue is a milestone toward
|
||||||
|
> the next. Dependencies are explicit: later stages require earlier stages complete.
|
||||||
|
|
@ -0,0 +1,421 @@
|
||||||
|
Flat profile:
|
||||||
|
|
||||||
|
Each sample counts as 0.01 seconds.
|
||||||
|
% cumulative self self total
|
||||||
|
time seconds seconds calls ms/call ms/call name
|
||||||
|
22.22 0.02 0.02 1064503 0.00 0.00 SourceFile::offsetToLocation(int) const
|
||||||
|
11.11 0.03 0.01 1059636 0.00 0.00 Tokenizer::scope()
|
||||||
|
11.11 0.04 0.01 1059635 0.00 0.00 Lexer::isNumeric()
|
||||||
|
11.11 0.05 0.01 461302 0.00 0.00 DelimiterToken::~DelimiterToken()
|
||||||
|
11.11 0.06 0.01 278426 0.00 0.00 DelimiterToken::DelimiterToken()
|
||||||
|
11.11 0.07 0.01 133847 0.00 0.00 Lexer::readNumeric()
|
||||||
|
11.11 0.08 0.01 1 10.00 90.00 cmdTokens(CliArgs const&)
|
||||||
|
11.11 0.09 0.01 1 10.00 70.00 Tokenizer::scan(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >)
|
||||||
|
0.00 0.09 0.00 2754372 0.00 0.00 Lexer::isEnd()
|
||||||
|
0.00 0.09 0.00 2620524 0.00 0.00 Lexer::getchar()
|
||||||
|
0.00 0.09 0.00 2119274 0.00 0.00 Lexer::include(std::basic_string_view<char, std::char_traits<char> >, bool)
|
||||||
|
0.00 0.09 0.00 1851576 0.00 0.00 Lexer::getOffset()
|
||||||
|
0.00 0.09 0.00 1059640 0.00 0.00 Lexer::skipWhiteSpace()
|
||||||
|
0.00 0.09 0.00 991432 0.00 0.00 std::__detail::_Map_base<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, int>, std::allocator<std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, int> >, std::__detail::_Select1st, std::equal_to<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::hash<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, std::__detail::_Hashtable_traits<true, false, true>, true>::operator[](std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)
|
||||||
|
0.00 0.09 0.00 925788 0.00 0.00 Lexer::getchar(int)
|
||||||
|
0.00 0.09 0.00 691504 0.00 0.00 Lexer::getLocation()
|
||||||
|
0.00 0.09 0.00 691504 0.00 0.00 Lexer::toChar(int)
|
||||||
|
0.00 0.09 0.00 400817 0.00 0.00 Lexer::nextChar()
|
||||||
|
0.00 0.09 0.00 234284 0.00 0.00 IdentifierToken::~IdentifierToken()
|
||||||
|
0.00 0.09 0.00 234284 0.00 0.00 Lexer::beginPosition()
|
||||||
|
0.00 0.09 0.00 234284 0.00 0.00 Lexer::acceptPosition()
|
||||||
|
0.00 0.09 0.00 234284 0.00 0.00 Tokenizer::readIdentifier()
|
||||||
|
0.00 0.09 0.00 230202 0.00 0.00 OperatorToken::OperatorToken()
|
||||||
|
0.00 0.09 0.00 230202 0.00 0.00 OperatorToken::~OperatorToken()
|
||||||
|
0.00 0.09 0.00 133847 0.00 0.00 NumberToken::~NumberToken()
|
||||||
|
0.00 0.09 0.00 58981 0.00 0.00 KeywordToken::~KeywordToken()
|
||||||
|
0.00 0.09 0.00 21 0.00 0.00 void std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::_M_construct<true>(char const*, unsigned long)
|
||||||
|
0.00 0.09 0.00 20 0.00 0.00 std::_Function_handler<int (CliArgs const&), main::{lambda(CliArgs const&)#4}>::_M_invoke(std::_Any_data const&, CliArgs const&)
|
||||||
|
0.00 0.09 0.00 19 0.00 0.00 std::_Function_handler<int (CliArgs const&), int (*)(CliArgs const&)>::_M_manager(std::_Any_data&, std::_Any_data const&, std::_Manager_operation)
|
||||||
|
0.00 0.09 0.00 11 0.00 0.00 bool std::operator==<char, std::char_traits<char>, std::allocator<char> >(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, char const*)
|
||||||
|
0.00 0.09 0.00 9 0.00 0.00 CliCommand::~CliCommand()
|
||||||
|
0.00 0.09 0.00 7 0.00 0.00 std::vector<CliCommand, std::allocator<CliCommand> >::push_back(CliCommand const&)
|
||||||
|
0.00 0.09 0.00 3 0.00 0.00 std::_Function_handler<int (CliArgs const&), main::{lambda(CliArgs const&)#2}>::_M_manager(std::_Any_data&, std::_Any_data const&, std::_Manager_operation)
|
||||||
|
0.00 0.09 0.00 3 0.00 0.00 std::_Function_handler<int (CliArgs const&), main::{lambda(CliArgs const&)#3}>::_M_manager(std::_Any_data&, std::_Any_data const&, std::_Manager_operation)
|
||||||
|
0.00 0.09 0.00 3 0.00 0.00 std::_Function_handler<int (CliArgs const&), main::{lambda(CliArgs const&)#4}>::_M_manager(std::_Any_data&, std::_Any_data const&, std::_Manager_operation)
|
||||||
|
0.00 0.09 0.00 3 0.00 0.00 std::_Function_handler<int (CliArgs const&), main::{lambda(CliArgs const&)#1}>::_M_manager(std::_Any_data&, std::_Any_data const&, std::_Manager_operation)
|
||||||
|
0.00 0.09 0.00 2 0.00 0.00 std::_Hashtable<TokenType, std::pair<TokenType const, std::basic_string_view<char, std::char_traits<char> > >, std::allocator<std::pair<TokenType const, std::basic_string_view<char, std::char_traits<char> > > >, std::__detail::_Select1st, std::equal_to<TokenType>, std::hash<TokenType>, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, std::__detail::_Hashtable_traits<false, false, true> >::_Hashtable<std::pair<TokenType const, std::basic_string_view<char, std::char_traits<char> > > const*>(std::pair<TokenType const, std::basic_string_view<char, std::char_traits<char> > > const*, std::pair<TokenType const, std::basic_string_view<char, std::char_traits<char> > > const*, unsigned long, std::hash<TokenType> const&, std::equal_to<TokenType> const&, std::allocator<std::pair<TokenType const, std::basic_string_view<char, std::char_traits<char> > > > const&, std::integral_constant<bool, true>)
|
||||||
|
0.00 0.09 0.00 2 0.00 0.00 std::_Hashtable<std::basic_string_view<char, std::char_traits<char> >, std::pair<std::basic_string_view<char, std::char_traits<char> > const, TokenType>, std::allocator<std::pair<std::basic_string_view<char, std::char_traits<char> > const, TokenType> >, std::__detail::_Select1st, std::equal_to<std::basic_string_view<char, std::char_traits<char> > >, std::hash<std::basic_string_view<char, std::char_traits<char> > >, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, std::__detail::_Hashtable_traits<true, false, true> >::_Hashtable<std::pair<std::basic_string_view<char, std::char_traits<char> > const, TokenType> const*>(std::pair<std::basic_string_view<char, std::char_traits<char> > const, TokenType> const*, std::pair<std::basic_string_view<char, std::char_traits<char> > const, TokenType> const*, unsigned long, std::hash<std::basic_string_view<char, std::char_traits<char> > > const&, std::equal_to<std::basic_string_view<char, std::char_traits<char> > > const&, std::allocator<std::pair<std::basic_string_view<char, std::char_traits<char> > const, TokenType> > const&, std::integral_constant<bool, true>)
|
||||||
|
0.00 0.09 0.00 1 0.00 0.00 readSource[abi:cxx11](CliArgs const&)
|
||||||
|
0.00 0.09 0.00 1 0.00 0.00 parseArgs(int, char**)
|
||||||
|
0.00 0.09 0.00 1 0.00 0.00 SourceFile::computeLineStarts()
|
||||||
|
0.00 0.09 0.00 1 0.00 0.00 SourceFile::setText(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)
|
||||||
|
0.00 0.09 0.00 1 0.00 0.00 Lexer::setSourceText(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)
|
||||||
|
0.00 0.09 0.00 1 0.00 0.00 std::_Function_handler<int (CliArgs const&), int (*)(CliArgs const&)>::_M_invoke(std::_Any_data const&, CliArgs const&)
|
||||||
|
|
||||||
|
% the percentage of the total running time of the
|
||||||
|
time program used by this function.
|
||||||
|
|
||||||
|
cumulative a running sum of the number of seconds accounted
|
||||||
|
seconds for by this function and those listed above it.
|
||||||
|
|
||||||
|
self the number of seconds accounted for by this
|
||||||
|
seconds function alone. This is the major sort for this
|
||||||
|
listing.
|
||||||
|
|
||||||
|
calls the number of times this function was invoked, if
|
||||||
|
this function is profiled, else blank.
|
||||||
|
|
||||||
|
self the average number of milliseconds spent in this
|
||||||
|
ms/call function per call, if this function is profiled,
|
||||||
|
else blank.
|
||||||
|
|
||||||
|
total the average number of milliseconds spent in this
|
||||||
|
ms/call function and its descendents per call, if this
|
||||||
|
function is profiled, else blank.
|
||||||
|
|
||||||
|
name the name of the function. This is the minor sort
|
||||||
|
for this listing. The index shows the location of
|
||||||
|
the function in the gprof listing. If the index is
|
||||||
|
in parenthesis it shows where it would appear in
|
||||||
|
the gprof listing if it were to be printed.
|
||||||
|
|
||||||
|
Copyright (C) 2012-2026 Free Software Foundation, Inc.
|
||||||
|
|
||||||
|
Copying and distribution of this file, with or without modification,
|
||||||
|
are permitted in any medium without royalty provided the copyright
|
||||||
|
notice and this notice are preserved.
|
||||||
|
|
||||||
|
Call graph (explanation follows)
|
||||||
|
|
||||||
|
|
||||||
|
granularity: each sample hit covers 4 byte(s) for 11.11% of 0.09 seconds
|
||||||
|
|
||||||
|
index % time self children called name
|
||||||
|
<spontaneous>
|
||||||
|
[1] 100.0 0.00 0.09 main [1]
|
||||||
|
0.01 0.08 1/1 cmdTokens(CliArgs const&) [2]
|
||||||
|
0.00 0.00 18/20 std::_Function_handler<int (CliArgs const&), main::{lambda(CliArgs const&)#4}>::_M_invoke(std::_Any_data const&, CliArgs const&) [35]
|
||||||
|
0.00 0.00 9/9 CliCommand::~CliCommand() [38]
|
||||||
|
0.00 0.00 7/19 std::_Function_handler<int (CliArgs const&), int (*)(CliArgs const&)>::_M_manager(std::_Any_data&, std::_Any_data const&, std::_Manager_operation) [36]
|
||||||
|
0.00 0.00 7/7 std::vector<CliCommand, std::allocator<CliCommand> >::push_back(CliCommand const&) [39]
|
||||||
|
0.00 0.00 4/21 void std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::_M_construct<true>(char const*, unsigned long) [34]
|
||||||
|
0.00 0.00 1/1 parseArgs(int, char**) [47]
|
||||||
|
0.00 0.00 1/11 bool std::operator==<char, std::char_traits<char>, std::allocator<char> >(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, char const*) [37]
|
||||||
|
0.00 0.00 1/1 std::_Function_handler<int (CliArgs const&), int (*)(CliArgs const&)>::_M_invoke(std::_Any_data const&, CliArgs const&) [51]
|
||||||
|
0.00 0.00 1/3 std::_Function_handler<int (CliArgs const&), main::{lambda(CliArgs const&)#4}>::_M_manager(std::_Any_data&, std::_Any_data const&, std::_Manager_operation) [42]
|
||||||
|
0.00 0.00 1/3 std::_Function_handler<int (CliArgs const&), main::{lambda(CliArgs const&)#3}>::_M_manager(std::_Any_data&, std::_Any_data const&, std::_Manager_operation) [41]
|
||||||
|
0.00 0.00 1/3 std::_Function_handler<int (CliArgs const&), main::{lambda(CliArgs const&)#2}>::_M_manager(std::_Any_data&, std::_Any_data const&, std::_Manager_operation) [40]
|
||||||
|
0.00 0.00 1/3 std::_Function_handler<int (CliArgs const&), main::{lambda(CliArgs const&)#1}>::_M_manager(std::_Any_data&, std::_Any_data const&, std::_Manager_operation) [43]
|
||||||
|
-----------------------------------------------
|
||||||
|
0.01 0.08 1/1 main [1]
|
||||||
|
[2] 100.0 0.01 0.08 1 cmdTokens(CliArgs const&) [2]
|
||||||
|
0.01 0.06 1/1 Tokenizer::scan(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >) [3]
|
||||||
|
0.01 0.00 461302/461302 DelimiterToken::~DelimiterToken() [9]
|
||||||
|
0.00 0.00 230202/230202 OperatorToken::~OperatorToken() [31]
|
||||||
|
0.00 0.00 175303/234284 IdentifierToken::~IdentifierToken() [27]
|
||||||
|
0.00 0.00 133847/133847 NumberToken::~NumberToken() [32]
|
||||||
|
0.00 0.00 58981/58981 KeywordToken::~KeywordToken() [33]
|
||||||
|
0.00 0.00 2/21 void std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::_M_construct<true>(char const*, unsigned long) [34]
|
||||||
|
0.00 0.00 1/1 readSource[abi:cxx11](CliArgs const&) [46]
|
||||||
|
-----------------------------------------------
|
||||||
|
0.01 0.06 1/1 cmdTokens(CliArgs const&) [2]
|
||||||
|
[3] 77.8 0.01 0.06 1 Tokenizer::scan(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >) [3]
|
||||||
|
0.01 0.05 1059636/1059636 Tokenizer::scope() [4]
|
||||||
|
0.00 0.00 1059635/2754372 Lexer::isEnd() [18]
|
||||||
|
0.00 0.00 1/1 Lexer::setSourceText(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) [50]
|
||||||
|
-----------------------------------------------
|
||||||
|
0.01 0.05 1059636/1059636 Tokenizer::scan(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >) [3]
|
||||||
|
[4] 66.7 0.01 0.05 1059636 Tokenizer::scope() [4]
|
||||||
|
0.00 0.01 691504/691504 Lexer::getLocation() [6]
|
||||||
|
0.01 0.00 133847/133847 Lexer::readNumeric() [7]
|
||||||
|
0.01 0.00 1059635/1059635 Lexer::isNumeric() [8]
|
||||||
|
0.01 0.00 278426/278426 DelimiterToken::DelimiterToken() [10]
|
||||||
|
0.00 0.00 234284/234284 Tokenizer::readIdentifier() [11]
|
||||||
|
0.00 0.00 2119274/2119274 Lexer::include(std::basic_string_view<char, std::char_traits<char> >, bool) [20]
|
||||||
|
0.00 0.00 1985561/2620524 Lexer::getchar() [19]
|
||||||
|
0.00 0.00 1383008/1851576 Lexer::getOffset() [21]
|
||||||
|
0.00 0.00 1059774/2754372 Lexer::isEnd() [18]
|
||||||
|
0.00 0.00 1059640/1059640 Lexer::skipWhiteSpace() [22]
|
||||||
|
0.00 0.00 991400/991432 std::__detail::_Map_base<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, int>, std::allocator<std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, int> >, std::__detail::_Select1st, std::equal_to<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::hash<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, std::__detail::_Hashtable_traits<true, false, true>, true>::operator[](std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) [23]
|
||||||
|
0.00 0.00 925788/925788 Lexer::getchar(int) [24]
|
||||||
|
0.00 0.00 691504/691504 Lexer::toChar(int) [25]
|
||||||
|
0.00 0.00 230202/230202 OperatorToken::OperatorToken() [30]
|
||||||
|
0.00 0.00 58981/234284 IdentifierToken::~IdentifierToken() [27]
|
||||||
|
0.00 0.00 138/400817 Lexer::nextChar() [26]
|
||||||
|
-----------------------------------------------
|
||||||
|
0.00 0.00 138715/1064503 Lexer::readNumeric() [7]
|
||||||
|
0.00 0.00 234284/1064503 Tokenizer::readIdentifier() [11]
|
||||||
|
0.01 0.00 691504/1064503 Lexer::getLocation() [6]
|
||||||
|
[5] 22.2 0.02 0.00 1064503 SourceFile::offsetToLocation(int) const [5]
|
||||||
|
-----------------------------------------------
|
||||||
|
0.00 0.01 691504/691504 Tokenizer::scope() [4]
|
||||||
|
[6] 14.4 0.00 0.01 691504 Lexer::getLocation() [6]
|
||||||
|
0.01 0.00 691504/1064503 SourceFile::offsetToLocation(int) const [5]
|
||||||
|
-----------------------------------------------
|
||||||
|
0.01 0.00 133847/133847 Tokenizer::scope() [4]
|
||||||
|
[7] 14.0 0.01 0.00 133847 Lexer::readNumeric() [7]
|
||||||
|
0.00 0.00 138715/1064503 SourceFile::offsetToLocation(int) const [5]
|
||||||
|
-----------------------------------------------
|
||||||
|
0.01 0.00 1059635/1059635 Tokenizer::scope() [4]
|
||||||
|
[8] 11.1 0.01 0.00 1059635 Lexer::isNumeric() [8]
|
||||||
|
-----------------------------------------------
|
||||||
|
0.01 0.00 461302/461302 cmdTokens(CliArgs const&) [2]
|
||||||
|
[9] 11.1 0.01 0.00 461302 DelimiterToken::~DelimiterToken() [9]
|
||||||
|
-----------------------------------------------
|
||||||
|
0.01 0.00 278426/278426 Tokenizer::scope() [4]
|
||||||
|
[10] 11.1 0.01 0.00 278426 DelimiterToken::DelimiterToken() [10]
|
||||||
|
-----------------------------------------------
|
||||||
|
0.00 0.00 234284/234284 Tokenizer::scope() [4]
|
||||||
|
[11] 4.9 0.00 0.00 234284 Tokenizer::readIdentifier() [11]
|
||||||
|
0.00 0.00 234284/1064503 SourceFile::offsetToLocation(int) const [5]
|
||||||
|
0.00 0.00 634963/2754372 Lexer::isEnd() [18]
|
||||||
|
0.00 0.00 634963/2620524 Lexer::getchar() [19]
|
||||||
|
0.00 0.00 468568/1851576 Lexer::getOffset() [21]
|
||||||
|
0.00 0.00 400679/400817 Lexer::nextChar() [26]
|
||||||
|
0.00 0.00 234284/234284 Lexer::beginPosition() [28]
|
||||||
|
0.00 0.00 234284/234284 Lexer::acceptPosition() [29]
|
||||||
|
-----------------------------------------------
|
||||||
|
0.00 0.00 634963/2754372 Tokenizer::readIdentifier() [11]
|
||||||
|
0.00 0.00 1059635/2754372 Tokenizer::scan(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >) [3]
|
||||||
|
0.00 0.00 1059774/2754372 Tokenizer::scope() [4]
|
||||||
|
[18] 0.0 0.00 0.00 2754372 Lexer::isEnd() [18]
|
||||||
|
-----------------------------------------------
|
||||||
|
0.00 0.00 634963/2620524 Tokenizer::readIdentifier() [11]
|
||||||
|
0.00 0.00 1985561/2620524 Tokenizer::scope() [4]
|
||||||
|
[19] 0.0 0.00 0.00 2620524 Lexer::getchar() [19]
|
||||||
|
-----------------------------------------------
|
||||||
|
0.00 0.00 2119274/2119274 Tokenizer::scope() [4]
|
||||||
|
[20] 0.0 0.00 0.00 2119274 Lexer::include(std::basic_string_view<char, std::char_traits<char> >, bool) [20]
|
||||||
|
-----------------------------------------------
|
||||||
|
0.00 0.00 468568/1851576 Tokenizer::readIdentifier() [11]
|
||||||
|
0.00 0.00 1383008/1851576 Tokenizer::scope() [4]
|
||||||
|
[21] 0.0 0.00 0.00 1851576 Lexer::getOffset() [21]
|
||||||
|
-----------------------------------------------
|
||||||
|
0.00 0.00 1059640/1059640 Tokenizer::scope() [4]
|
||||||
|
[22] 0.0 0.00 0.00 1059640 Lexer::skipWhiteSpace() [22]
|
||||||
|
-----------------------------------------------
|
||||||
|
0.00 0.00 32/991432 __static_initialization_and_destruction_0() [81]
|
||||||
|
0.00 0.00 991400/991432 Tokenizer::scope() [4]
|
||||||
|
[23] 0.0 0.00 0.00 991432 std::__detail::_Map_base<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, int>, std::allocator<std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, int> >, std::__detail::_Select1st, std::equal_to<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::hash<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, std::__detail::_Hashtable_traits<true, false, true>, true>::operator[](std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) [23]
|
||||||
|
-----------------------------------------------
|
||||||
|
0.00 0.00 925788/925788 Tokenizer::scope() [4]
|
||||||
|
[24] 0.0 0.00 0.00 925788 Lexer::getchar(int) [24]
|
||||||
|
-----------------------------------------------
|
||||||
|
0.00 0.00 691504/691504 Tokenizer::scope() [4]
|
||||||
|
[25] 0.0 0.00 0.00 691504 Lexer::toChar(int) [25]
|
||||||
|
-----------------------------------------------
|
||||||
|
0.00 0.00 138/400817 Tokenizer::scope() [4]
|
||||||
|
0.00 0.00 400679/400817 Tokenizer::readIdentifier() [11]
|
||||||
|
[26] 0.0 0.00 0.00 400817 Lexer::nextChar() [26]
|
||||||
|
-----------------------------------------------
|
||||||
|
0.00 0.00 58981/234284 Tokenizer::scope() [4]
|
||||||
|
0.00 0.00 175303/234284 cmdTokens(CliArgs const&) [2]
|
||||||
|
[27] 0.0 0.00 0.00 234284 IdentifierToken::~IdentifierToken() [27]
|
||||||
|
-----------------------------------------------
|
||||||
|
0.00 0.00 234284/234284 Tokenizer::readIdentifier() [11]
|
||||||
|
[28] 0.0 0.00 0.00 234284 Lexer::beginPosition() [28]
|
||||||
|
-----------------------------------------------
|
||||||
|
0.00 0.00 234284/234284 Tokenizer::readIdentifier() [11]
|
||||||
|
[29] 0.0 0.00 0.00 234284 Lexer::acceptPosition() [29]
|
||||||
|
-----------------------------------------------
|
||||||
|
0.00 0.00 230202/230202 Tokenizer::scope() [4]
|
||||||
|
[30] 0.0 0.00 0.00 230202 OperatorToken::OperatorToken() [30]
|
||||||
|
-----------------------------------------------
|
||||||
|
0.00 0.00 230202/230202 cmdTokens(CliArgs const&) [2]
|
||||||
|
[31] 0.0 0.00 0.00 230202 OperatorToken::~OperatorToken() [31]
|
||||||
|
-----------------------------------------------
|
||||||
|
0.00 0.00 133847/133847 cmdTokens(CliArgs const&) [2]
|
||||||
|
[32] 0.0 0.00 0.00 133847 NumberToken::~NumberToken() [32]
|
||||||
|
-----------------------------------------------
|
||||||
|
0.00 0.00 58981/58981 cmdTokens(CliArgs const&) [2]
|
||||||
|
[33] 0.0 0.00 0.00 58981 KeywordToken::~KeywordToken() [33]
|
||||||
|
-----------------------------------------------
|
||||||
|
0.00 0.00 1/21 readSource[abi:cxx11](CliArgs const&) [46]
|
||||||
|
0.00 0.00 2/21 cmdTokens(CliArgs const&) [2]
|
||||||
|
0.00 0.00 4/21 main [1]
|
||||||
|
0.00 0.00 14/21 std::vector<CliCommand, std::allocator<CliCommand> >::push_back(CliCommand const&) [39]
|
||||||
|
[34] 0.0 0.00 0.00 21 void std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::_M_construct<true>(char const*, unsigned long) [34]
|
||||||
|
-----------------------------------------------
|
||||||
|
0.00 0.00 2/20 parseArgs(int, char**) [47]
|
||||||
|
0.00 0.00 18/20 main [1]
|
||||||
|
[35] 0.0 0.00 0.00 20 std::_Function_handler<int (CliArgs const&), main::{lambda(CliArgs const&)#4}>::_M_invoke(std::_Any_data const&, CliArgs const&) [35]
|
||||||
|
-----------------------------------------------
|
||||||
|
0.00 0.00 3/19 std::vector<CliCommand, std::allocator<CliCommand> >::push_back(CliCommand const&) [39]
|
||||||
|
0.00 0.00 4/19 parseArgs(int, char**) [47]
|
||||||
|
0.00 0.00 5/19 CliCommand::~CliCommand() [38]
|
||||||
|
0.00 0.00 7/19 main [1]
|
||||||
|
[36] 0.0 0.00 0.00 19 std::_Function_handler<int (CliArgs const&), int (*)(CliArgs const&)>::_M_manager(std::_Any_data&, std::_Any_data const&, std::_Manager_operation) [36]
|
||||||
|
-----------------------------------------------
|
||||||
|
0.00 0.00 1/11 main [1]
|
||||||
|
0.00 0.00 10/11 parseArgs(int, char**) [47]
|
||||||
|
[37] 0.0 0.00 0.00 11 bool std::operator==<char, std::char_traits<char>, std::allocator<char> >(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, char const*) [37]
|
||||||
|
-----------------------------------------------
|
||||||
|
0.00 0.00 9/9 main [1]
|
||||||
|
[38] 0.0 0.00 0.00 9 CliCommand::~CliCommand() [38]
|
||||||
|
0.00 0.00 5/19 std::_Function_handler<int (CliArgs const&), int (*)(CliArgs const&)>::_M_manager(std::_Any_data&, std::_Any_data const&, std::_Manager_operation) [36]
|
||||||
|
0.00 0.00 1/3 std::_Function_handler<int (CliArgs const&), main::{lambda(CliArgs const&)#4}>::_M_manager(std::_Any_data&, std::_Any_data const&, std::_Manager_operation) [42]
|
||||||
|
0.00 0.00 1/3 std::_Function_handler<int (CliArgs const&), main::{lambda(CliArgs const&)#3}>::_M_manager(std::_Any_data&, std::_Any_data const&, std::_Manager_operation) [41]
|
||||||
|
0.00 0.00 1/3 std::_Function_handler<int (CliArgs const&), main::{lambda(CliArgs const&)#2}>::_M_manager(std::_Any_data&, std::_Any_data const&, std::_Manager_operation) [40]
|
||||||
|
0.00 0.00 1/3 std::_Function_handler<int (CliArgs const&), main::{lambda(CliArgs const&)#1}>::_M_manager(std::_Any_data&, std::_Any_data const&, std::_Manager_operation) [43]
|
||||||
|
-----------------------------------------------
|
||||||
|
0.00 0.00 7/7 main [1]
|
||||||
|
[39] 0.0 0.00 0.00 7 std::vector<CliCommand, std::allocator<CliCommand> >::push_back(CliCommand const&) [39]
|
||||||
|
0.00 0.00 14/21 void std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::_M_construct<true>(char const*, unsigned long) [34]
|
||||||
|
0.00 0.00 3/19 std::_Function_handler<int (CliArgs const&), int (*)(CliArgs const&)>::_M_manager(std::_Any_data&, std::_Any_data const&, std::_Manager_operation) [36]
|
||||||
|
0.00 0.00 1/3 std::_Function_handler<int (CliArgs const&), main::{lambda(CliArgs const&)#3}>::_M_manager(std::_Any_data&, std::_Any_data const&, std::_Manager_operation) [41]
|
||||||
|
0.00 0.00 1/3 std::_Function_handler<int (CliArgs const&), main::{lambda(CliArgs const&)#2}>::_M_manager(std::_Any_data&, std::_Any_data const&, std::_Manager_operation) [40]
|
||||||
|
0.00 0.00 1/3 std::_Function_handler<int (CliArgs const&), main::{lambda(CliArgs const&)#1}>::_M_manager(std::_Any_data&, std::_Any_data const&, std::_Manager_operation) [43]
|
||||||
|
0.00 0.00 1/3 std::_Function_handler<int (CliArgs const&), main::{lambda(CliArgs const&)#4}>::_M_manager(std::_Any_data&, std::_Any_data const&, std::_Manager_operation) [42]
|
||||||
|
-----------------------------------------------
|
||||||
|
0.00 0.00 1/3 main [1]
|
||||||
|
0.00 0.00 1/3 CliCommand::~CliCommand() [38]
|
||||||
|
0.00 0.00 1/3 std::vector<CliCommand, std::allocator<CliCommand> >::push_back(CliCommand const&) [39]
|
||||||
|
[40] 0.0 0.00 0.00 3 std::_Function_handler<int (CliArgs const&), main::{lambda(CliArgs const&)#2}>::_M_manager(std::_Any_data&, std::_Any_data const&, std::_Manager_operation) [40]
|
||||||
|
-----------------------------------------------
|
||||||
|
0.00 0.00 1/3 main [1]
|
||||||
|
0.00 0.00 1/3 CliCommand::~CliCommand() [38]
|
||||||
|
0.00 0.00 1/3 std::vector<CliCommand, std::allocator<CliCommand> >::push_back(CliCommand const&) [39]
|
||||||
|
[41] 0.0 0.00 0.00 3 std::_Function_handler<int (CliArgs const&), main::{lambda(CliArgs const&)#3}>::_M_manager(std::_Any_data&, std::_Any_data const&, std::_Manager_operation) [41]
|
||||||
|
-----------------------------------------------
|
||||||
|
0.00 0.00 1/3 main [1]
|
||||||
|
0.00 0.00 1/3 CliCommand::~CliCommand() [38]
|
||||||
|
0.00 0.00 1/3 std::vector<CliCommand, std::allocator<CliCommand> >::push_back(CliCommand const&) [39]
|
||||||
|
[42] 0.0 0.00 0.00 3 std::_Function_handler<int (CliArgs const&), main::{lambda(CliArgs const&)#4}>::_M_manager(std::_Any_data&, std::_Any_data const&, std::_Manager_operation) [42]
|
||||||
|
-----------------------------------------------
|
||||||
|
0.00 0.00 1/3 main [1]
|
||||||
|
0.00 0.00 1/3 CliCommand::~CliCommand() [38]
|
||||||
|
0.00 0.00 1/3 std::vector<CliCommand, std::allocator<CliCommand> >::push_back(CliCommand const&) [39]
|
||||||
|
[43] 0.0 0.00 0.00 3 std::_Function_handler<int (CliArgs const&), main::{lambda(CliArgs const&)#1}>::_M_manager(std::_Any_data&, std::_Any_data const&, std::_Manager_operation) [43]
|
||||||
|
-----------------------------------------------
|
||||||
|
0.00 0.00 2/2 __static_initialization_and_destruction_0() [69]
|
||||||
|
[44] 0.0 0.00 0.00 2 std::_Hashtable<TokenType, std::pair<TokenType const, std::basic_string_view<char, std::char_traits<char> > >, std::allocator<std::pair<TokenType const, std::basic_string_view<char, std::char_traits<char> > > >, std::__detail::_Select1st, std::equal_to<TokenType>, std::hash<TokenType>, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, std::__detail::_Hashtable_traits<false, false, true> >::_Hashtable<std::pair<TokenType const, std::basic_string_view<char, std::char_traits<char> > > const*>(std::pair<TokenType const, std::basic_string_view<char, std::char_traits<char> > > const*, std::pair<TokenType const, std::basic_string_view<char, std::char_traits<char> > > const*, unsigned long, std::hash<TokenType> const&, std::equal_to<TokenType> const&, std::allocator<std::pair<TokenType const, std::basic_string_view<char, std::char_traits<char> > > > const&, std::integral_constant<bool, true>) [44]
|
||||||
|
-----------------------------------------------
|
||||||
|
0.00 0.00 2/2 __static_initialization_and_destruction_0() [69]
|
||||||
|
[45] 0.0 0.00 0.00 2 std::_Hashtable<std::basic_string_view<char, std::char_traits<char> >, std::pair<std::basic_string_view<char, std::char_traits<char> > const, TokenType>, std::allocator<std::pair<std::basic_string_view<char, std::char_traits<char> > const, TokenType> >, std::__detail::_Select1st, std::equal_to<std::basic_string_view<char, std::char_traits<char> > >, std::hash<std::basic_string_view<char, std::char_traits<char> > >, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, std::__detail::_Hashtable_traits<true, false, true> >::_Hashtable<std::pair<std::basic_string_view<char, std::char_traits<char> > const, TokenType> const*>(std::pair<std::basic_string_view<char, std::char_traits<char> > const, TokenType> const*, std::pair<std::basic_string_view<char, std::char_traits<char> > const, TokenType> const*, unsigned long, std::hash<std::basic_string_view<char, std::char_traits<char> > > const&, std::equal_to<std::basic_string_view<char, std::char_traits<char> > > const&, std::allocator<std::pair<std::basic_string_view<char, std::char_traits<char> > const, TokenType> > const&, std::integral_constant<bool, true>) [45]
|
||||||
|
-----------------------------------------------
|
||||||
|
0.00 0.00 1/1 cmdTokens(CliArgs const&) [2]
|
||||||
|
[46] 0.0 0.00 0.00 1 readSource[abi:cxx11](CliArgs const&) [46]
|
||||||
|
0.00 0.00 1/21 void std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::_M_construct<true>(char const*, unsigned long) [34]
|
||||||
|
-----------------------------------------------
|
||||||
|
0.00 0.00 1/1 main [1]
|
||||||
|
[47] 0.0 0.00 0.00 1 parseArgs(int, char**) [47]
|
||||||
|
0.00 0.00 10/11 bool std::operator==<char, std::char_traits<char>, std::allocator<char> >(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, char const*) [37]
|
||||||
|
0.00 0.00 4/19 std::_Function_handler<int (CliArgs const&), int (*)(CliArgs const&)>::_M_manager(std::_Any_data&, std::_Any_data const&, std::_Manager_operation) [36]
|
||||||
|
0.00 0.00 2/20 std::_Function_handler<int (CliArgs const&), main::{lambda(CliArgs const&)#4}>::_M_invoke(std::_Any_data const&, CliArgs const&) [35]
|
||||||
|
-----------------------------------------------
|
||||||
|
0.00 0.00 1/1 Lexer::setSourceText(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) [50]
|
||||||
|
[48] 0.0 0.00 0.00 1 SourceFile::computeLineStarts() [48]
|
||||||
|
-----------------------------------------------
|
||||||
|
0.00 0.00 1/1 Lexer::setSourceText(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) [50]
|
||||||
|
[49] 0.0 0.00 0.00 1 SourceFile::setText(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) [49]
|
||||||
|
-----------------------------------------------
|
||||||
|
0.00 0.00 1/1 Tokenizer::scan(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >) [3]
|
||||||
|
[50] 0.0 0.00 0.00 1 Lexer::setSourceText(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) [50]
|
||||||
|
0.00 0.00 1/1 SourceFile::computeLineStarts() [48]
|
||||||
|
0.00 0.00 1/1 SourceFile::setText(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) [49]
|
||||||
|
-----------------------------------------------
|
||||||
|
0.00 0.00 1/1 main [1]
|
||||||
|
[51] 0.0 0.00 0.00 1 std::_Function_handler<int (CliArgs const&), int (*)(CliArgs const&)>::_M_invoke(std::_Any_data const&, CliArgs const&) [51]
|
||||||
|
-----------------------------------------------
|
||||||
|
|
||||||
|
This table describes the call tree of the program, and was sorted by
|
||||||
|
the total amount of time spent in each function and its children.
|
||||||
|
|
||||||
|
Each entry in this table consists of several lines. The line with the
|
||||||
|
index number at the left hand margin lists the current function.
|
||||||
|
The lines above it list the functions that called this function,
|
||||||
|
and the lines below it list the functions this one called.
|
||||||
|
This line lists:
|
||||||
|
index A unique number given to each element of the table.
|
||||||
|
Index numbers are sorted numerically.
|
||||||
|
The index number is printed next to every function name so
|
||||||
|
it is easier to look up where the function is in the table.
|
||||||
|
|
||||||
|
% time This is the percentage of the `total' time that was spent
|
||||||
|
in this function and its children. Note that due to
|
||||||
|
different viewpoints, functions excluded by options, etc,
|
||||||
|
these numbers will NOT add up to 100%.
|
||||||
|
|
||||||
|
self This is the total amount of time spent in this function.
|
||||||
|
|
||||||
|
children This is the total amount of time propagated into this
|
||||||
|
function by its children.
|
||||||
|
|
||||||
|
called This is the number of times the function was called.
|
||||||
|
If the function called itself recursively, the number
|
||||||
|
only includes non-recursive calls, and is followed by
|
||||||
|
a `+' and the number of recursive calls.
|
||||||
|
|
||||||
|
name The name of the current function. The index number is
|
||||||
|
printed after it. If the function is a member of a
|
||||||
|
cycle, the cycle number is printed between the
|
||||||
|
function's name and the index number.
|
||||||
|
|
||||||
|
|
||||||
|
For the function's parents, the fields have the following meanings:
|
||||||
|
|
||||||
|
self This is the amount of time that was propagated directly
|
||||||
|
from the function into this parent.
|
||||||
|
|
||||||
|
children This is the amount of time that was propagated from
|
||||||
|
the function's children into this parent.
|
||||||
|
|
||||||
|
called This is the number of times this parent called the
|
||||||
|
function `/' the total number of times the function
|
||||||
|
was called. Recursive calls to the function are not
|
||||||
|
included in the number after the `/'.
|
||||||
|
|
||||||
|
name This is the name of the parent. The parent's index
|
||||||
|
number is printed after it. If the parent is a
|
||||||
|
member of a cycle, the cycle number is printed between
|
||||||
|
the name and the index number.
|
||||||
|
|
||||||
|
If the parents of the function cannot be determined, the word
|
||||||
|
`<spontaneous>' is printed in the `name' field, and all the other
|
||||||
|
fields are blank.
|
||||||
|
|
||||||
|
For the function's children, the fields have the following meanings:
|
||||||
|
|
||||||
|
self This is the amount of time that was propagated directly
|
||||||
|
from the child into the function.
|
||||||
|
|
||||||
|
children This is the amount of time that was propagated from the
|
||||||
|
child's children to the function.
|
||||||
|
|
||||||
|
called This is the number of times the function called
|
||||||
|
this child `/' the total number of times the child
|
||||||
|
was called. Recursive calls by the child are not
|
||||||
|
listed in the number after the `/'.
|
||||||
|
|
||||||
|
name This is the name of the child. The child's index
|
||||||
|
number is printed after it. If the child is a
|
||||||
|
member of a cycle, the cycle number is printed
|
||||||
|
between the name and the index number.
|
||||||
|
|
||||||
|
If there are any cycles (circles) in the call graph, there is an
|
||||||
|
entry for the cycle-as-a-whole. This entry shows who called the
|
||||||
|
cycle (as parents) and the members of the cycle (as children.)
|
||||||
|
The `+' recursive calls entry shows the number of function calls that
|
||||||
|
were internal to the cycle, and the calls entry for each member shows,
|
||||||
|
for that member, how many times it was called from other members of
|
||||||
|
the cycle.
|
||||||
|
|
||||||
|
Copyright (C) 2012-2026 Free Software Foundation, Inc.
|
||||||
|
|
||||||
|
Copying and distribution of this file, with or without modification,
|
||||||
|
are permitted in any medium without royalty provided the copyright
|
||||||
|
notice and this notice are preserved.
|
||||||
|
|
||||||
|
Index by function name
|
||||||
|
|
||||||
|
[46] readSource[abi:cxx11](CliArgs const&) [28] Lexer::beginPosition() [5] SourceFile::offsetToLocation(int) const
|
||||||
|
[2] cmdTokens(CliArgs const&) [50] Lexer::setSourceText(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) [44] std::_Hashtable<TokenType, std::pair<TokenType const, std::basic_string_view<char, std::char_traits<char> > >, std::allocator<std::pair<TokenType const, std::basic_string_view<char, std::char_traits<char> > > >, std::__detail::_Select1st, std::equal_to<TokenType>, std::hash<TokenType>, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, std::__detail::_Hashtable_traits<false, false, true> >::_Hashtable<std::pair<TokenType const, std::basic_string_view<char, std::char_traits<char> > > const*>(std::pair<TokenType const, std::basic_string_view<char, std::char_traits<char> > > const*, std::pair<TokenType const, std::basic_string_view<char, std::char_traits<char> > > const*, unsigned long, std::hash<TokenType> const&, std::equal_to<TokenType> const&, std::allocator<std::pair<TokenType const, std::basic_string_view<char, std::char_traits<char> > > > const&, std::integral_constant<bool, true>)
|
||||||
|
[47] parseArgs(int, char**) [29] Lexer::acceptPosition() [45] std::_Hashtable<std::basic_string_view<char, std::char_traits<char> >, std::pair<std::basic_string_view<char, std::char_traits<char> > const, TokenType>, std::allocator<std::pair<std::basic_string_view<char, std::char_traits<char> > const, TokenType> >, std::__detail::_Select1st, std::equal_to<std::basic_string_view<char, std::char_traits<char> > >, std::hash<std::basic_string_view<char, std::char_traits<char> > >, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, std::__detail::_Hashtable_traits<true, false, true> >::_Hashtable<std::pair<std::basic_string_view<char, std::char_traits<char> > const, TokenType> const*>(std::pair<std::basic_string_view<char, std::char_traits<char> > const, TokenType> const*, std::pair<std::basic_string_view<char, std::char_traits<char> > const, TokenType> const*, unsigned long, std::hash<std::basic_string_view<char, std::char_traits<char> > > const&, std::equal_to<std::basic_string_view<char, std::char_traits<char> > > const&, std::allocator<std::pair<std::basic_string_view<char, std::char_traits<char> > const, TokenType> > const&, std::integral_constant<bool, true>)
|
||||||
|
[38] CliCommand::~CliCommand() [22] Lexer::skipWhiteSpace() [36] std::_Function_handler<int (CliArgs const&), int (*)(CliArgs const&)>::_M_manager(std::_Any_data&, std::_Any_data const&, std::_Manager_operation)
|
||||||
|
[48] SourceFile::computeLineStarts() [18] Lexer::isEnd() [51] std::_Function_handler<int (CliArgs const&), int (*)(CliArgs const&)>::_M_invoke(std::_Any_data const&, CliArgs const&)
|
||||||
|
[49] SourceFile::setText(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) [25] Lexer::toChar(int) [40] std::_Function_handler<int (CliArgs const&), main::{lambda(CliArgs const&)#2}>::_M_manager(std::_Any_data&, std::_Any_data const&, std::_Manager_operation) (std_function.h)
|
||||||
|
[32] NumberToken::~NumberToken() [24] Lexer::getchar(int) [41] std::_Function_handler<int (CliArgs const&), main::{lambda(CliArgs const&)#3}>::_M_manager(std::_Any_data&, std::_Any_data const&, std::_Manager_operation) (std_function.h)
|
||||||
|
[33] KeywordToken::~KeywordToken() [19] Lexer::getchar() [42] std::_Function_handler<int (CliArgs const&), main::{lambda(CliArgs const&)#4}>::_M_manager(std::_Any_data&, std::_Any_data const&, std::_Manager_operation) (std_function.h)
|
||||||
|
[30] OperatorToken::OperatorToken() [20] Lexer::include(std::basic_string_view<char, std::char_traits<char> >, bool) [35] std::_Function_handler<int (CliArgs const&), main::{lambda(CliArgs const&)#4}>::_M_invoke(std::_Any_data const&, CliArgs const&) (std_function.h)
|
||||||
|
[31] OperatorToken::~OperatorToken() [26] Lexer::nextChar() [43] std::_Function_handler<int (CliArgs const&), main::{lambda(CliArgs const&)#1}>::_M_manager(std::_Any_data&, std::_Any_data const&, std::_Manager_operation) (std_function.h)
|
||||||
|
[10] DelimiterToken::DelimiterToken() [21] Lexer::getOffset() [39] std::vector<CliCommand, std::allocator<CliCommand> >::push_back(CliCommand const&)
|
||||||
|
[9] DelimiterToken::~DelimiterToken() [8] Lexer::isNumeric() [34] void std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::_M_construct<true>(char const*, unsigned long)
|
||||||
|
[27] IdentifierToken::~IdentifierToken() [11] Tokenizer::readIdentifier() [23] std::__detail::_Map_base<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, int>, std::allocator<std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, int> >, std::__detail::_Select1st, std::equal_to<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::hash<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, std::__detail::_Hashtable_traits<true, false, true>, true>::operator[](std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)
|
||||||
|
[6] Lexer::getLocation() [3] Tokenizer::scan(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >) [37] bool std::operator==<char, std::char_traits<char>, std::allocator<char> >(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, char const*)
|
||||||
|
[7] Lexer::readNumeric() [4] Tokenizer::scope()
|
||||||
|
|
@ -0,0 +1,406 @@
|
||||||
|
Flat profile:
|
||||||
|
|
||||||
|
Each sample counts as 0.01 seconds.
|
||||||
|
% cumulative self self total
|
||||||
|
time seconds seconds calls ms/call ms/call name
|
||||||
|
81.25 0.26 0.26 66967977 0.00 0.00 Lexer::include(std::basic_string_view<char, std::char_traits<char> >, bool)
|
||||||
|
9.38 0.29 0.03 1059636 0.00 0.00 Tokenizer::scope()
|
||||||
|
6.25 0.31 0.02 _init
|
||||||
|
3.12 0.32 0.01 1064503 0.00 0.00 SourceFile::offsetToLocation(int) const
|
||||||
|
0.00 0.32 0.00 2509314 0.00 0.00 Lexer::isEnd()
|
||||||
|
0.00 0.32 0.00 1851576 0.00 0.00 Lexer::getOffset()
|
||||||
|
0.00 0.32 0.00 1449678 0.00 0.00 Lexer::getchar()
|
||||||
|
0.00 0.32 0.00 1059640 0.00 0.00 Lexer::skipWhiteSpace()
|
||||||
|
0.00 0.32 0.00 1059635 0.00 0.00 Lexer::isNumeric()
|
||||||
|
0.00 0.32 0.00 750485 0.00 0.00 Lexer::getLocation()
|
||||||
|
0.00 0.32 0.00 750485 0.00 0.00 Lexer::toChar(int)
|
||||||
|
0.00 0.32 0.00 461302 0.00 0.00 DelimiterToken::~DelimiterToken()
|
||||||
|
0.00 0.32 0.00 230202 0.00 0.00 OperatorToken::~OperatorToken()
|
||||||
|
0.00 0.32 0.00 214740 0.00 0.00 Lexer::nextChar()
|
||||||
|
0.00 0.32 0.00 175303 0.00 0.00 IdentifierToken::~IdentifierToken()
|
||||||
|
0.00 0.32 0.00 175303 0.00 0.00 Lexer::beginPosition()
|
||||||
|
0.00 0.32 0.00 175303 0.00 0.00 Lexer::acceptPosition()
|
||||||
|
0.00 0.32 0.00 175303 0.00 0.00 Tokenizer::readIdentifier()
|
||||||
|
0.00 0.32 0.00 133847 0.00 0.00 NumberToken::~NumberToken()
|
||||||
|
0.00 0.32 0.00 133847 0.00 0.00 Lexer::readNumeric()
|
||||||
|
0.00 0.32 0.00 58981 0.00 0.00 KeywordToken::~KeywordToken()
|
||||||
|
0.00 0.32 0.00 58981 0.00 0.00 Lexer::getchar(int)
|
||||||
|
0.00 0.32 0.00 21 0.00 0.00 void std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::_M_construct<true>(char const*, unsigned long)
|
||||||
|
0.00 0.32 0.00 20 0.00 0.00 std::_Function_handler<int (CliArgs const&), main::{lambda(CliArgs const&)#4}>::_M_invoke(std::_Any_data const&, CliArgs const&)
|
||||||
|
0.00 0.32 0.00 19 0.00 0.00 std::_Function_handler<int (CliArgs const&), int (*)(CliArgs const&)>::_M_manager(std::_Any_data&, std::_Any_data const&, std::_Manager_operation)
|
||||||
|
0.00 0.32 0.00 11 0.00 0.00 bool std::operator==<char, std::char_traits<char>, std::allocator<char> >(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, char const*)
|
||||||
|
0.00 0.32 0.00 9 0.00 0.00 CliCommand::~CliCommand()
|
||||||
|
0.00 0.32 0.00 7 0.00 0.00 std::vector<CliCommand, std::allocator<CliCommand> >::push_back(CliCommand const&)
|
||||||
|
0.00 0.32 0.00 3 0.00 0.00 std::_Function_handler<int (CliArgs const&), main::{lambda(CliArgs const&)#2}>::_M_manager(std::_Any_data&, std::_Any_data const&, std::_Manager_operation)
|
||||||
|
0.00 0.32 0.00 3 0.00 0.00 std::_Function_handler<int (CliArgs const&), main::{lambda(CliArgs const&)#3}>::_M_manager(std::_Any_data&, std::_Any_data const&, std::_Manager_operation)
|
||||||
|
0.00 0.32 0.00 3 0.00 0.00 std::_Function_handler<int (CliArgs const&), main::{lambda(CliArgs const&)#4}>::_M_manager(std::_Any_data&, std::_Any_data const&, std::_Manager_operation)
|
||||||
|
0.00 0.32 0.00 3 0.00 0.00 std::_Function_handler<int (CliArgs const&), main::{lambda(CliArgs const&)#1}>::_M_manager(std::_Any_data&, std::_Any_data const&, std::_Manager_operation)
|
||||||
|
0.00 0.32 0.00 2 0.00 0.00 std::_Hashtable<TokenType, std::pair<TokenType const, std::basic_string_view<char, std::char_traits<char> > >, std::allocator<std::pair<TokenType const, std::basic_string_view<char, std::char_traits<char> > > >, std::__detail::_Select1st, std::equal_to<TokenType>, std::hash<TokenType>, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, std::__detail::_Hashtable_traits<false, false, true> >::_Hashtable<std::pair<TokenType const, std::basic_string_view<char, std::char_traits<char> > > const*>(std::pair<TokenType const, std::basic_string_view<char, std::char_traits<char> > > const*, std::pair<TokenType const, std::basic_string_view<char, std::char_traits<char> > > const*, unsigned long, std::hash<TokenType> const&, std::equal_to<TokenType> const&, std::allocator<std::pair<TokenType const, std::basic_string_view<char, std::char_traits<char> > > > const&, std::integral_constant<bool, true>)
|
||||||
|
0.00 0.32 0.00 2 0.00 0.00 std::_Hashtable<std::basic_string_view<char, std::char_traits<char> >, std::pair<std::basic_string_view<char, std::char_traits<char> > const, TokenType>, std::allocator<std::pair<std::basic_string_view<char, std::char_traits<char> > const, TokenType> >, std::__detail::_Select1st, std::equal_to<std::basic_string_view<char, std::char_traits<char> > >, std::hash<std::basic_string_view<char, std::char_traits<char> > >, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, std::__detail::_Hashtable_traits<true, false, true> >::_Hashtable<std::pair<std::basic_string_view<char, std::char_traits<char> > const, TokenType> const*>(std::pair<std::basic_string_view<char, std::char_traits<char> > const, TokenType> const*, std::pair<std::basic_string_view<char, std::char_traits<char> > const, TokenType> const*, unsigned long, std::hash<std::basic_string_view<char, std::char_traits<char> > > const&, std::equal_to<std::basic_string_view<char, std::char_traits<char> > > const&, std::allocator<std::pair<std::basic_string_view<char, std::char_traits<char> > const, TokenType> > const&, std::integral_constant<bool, true>)
|
||||||
|
0.00 0.32 0.00 1 0.00 0.00 readSource[abi:cxx11](CliArgs const&)
|
||||||
|
0.00 0.32 0.00 1 0.00 300.00 cmdTokens(CliArgs const&)
|
||||||
|
0.00 0.32 0.00 1 0.00 0.00 parseArgs(int, char**)
|
||||||
|
0.00 0.32 0.00 1 0.00 0.00 SourceFile::computeLineStarts()
|
||||||
|
0.00 0.32 0.00 1 0.00 0.00 SourceFile::setText(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)
|
||||||
|
0.00 0.32 0.00 1 0.00 0.00 Lexer::setSourceText(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)
|
||||||
|
0.00 0.32 0.00 1 0.00 300.00 Tokenizer::scan(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >)
|
||||||
|
0.00 0.32 0.00 1 0.00 0.00 std::_Function_handler<int (CliArgs const&), int (*)(CliArgs const&)>::_M_invoke(std::_Any_data const&, CliArgs const&)
|
||||||
|
|
||||||
|
% the percentage of the total running time of the
|
||||||
|
time program used by this function.
|
||||||
|
|
||||||
|
cumulative a running sum of the number of seconds accounted
|
||||||
|
seconds for by this function and those listed above it.
|
||||||
|
|
||||||
|
self the number of seconds accounted for by this
|
||||||
|
seconds function alone. This is the major sort for this
|
||||||
|
listing.
|
||||||
|
|
||||||
|
calls the number of times this function was invoked, if
|
||||||
|
this function is profiled, else blank.
|
||||||
|
|
||||||
|
self the average number of milliseconds spent in this
|
||||||
|
ms/call function per call, if this function is profiled,
|
||||||
|
else blank.
|
||||||
|
|
||||||
|
total the average number of milliseconds spent in this
|
||||||
|
ms/call function and its descendents per call, if this
|
||||||
|
function is profiled, else blank.
|
||||||
|
|
||||||
|
name the name of the function. This is the minor sort
|
||||||
|
for this listing. The index shows the location of
|
||||||
|
the function in the gprof listing. If the index is
|
||||||
|
in parenthesis it shows where it would appear in
|
||||||
|
the gprof listing if it were to be printed.
|
||||||
|
|
||||||
|
Copyright (C) 2012-2026 Free Software Foundation, Inc.
|
||||||
|
|
||||||
|
Copying and distribution of this file, with or without modification,
|
||||||
|
are permitted in any medium without royalty provided the copyright
|
||||||
|
notice and this notice are preserved.
|
||||||
|
|
||||||
|
Call graph (explanation follows)
|
||||||
|
|
||||||
|
|
||||||
|
granularity: each sample hit covers 4 byte(s) for 3.12% of 0.32 seconds
|
||||||
|
|
||||||
|
index % time self children called name
|
||||||
|
<spontaneous>
|
||||||
|
[1] 93.8 0.00 0.30 main [1]
|
||||||
|
0.00 0.30 1/1 cmdTokens(CliArgs const&) [2]
|
||||||
|
0.00 0.00 18/20 std::_Function_handler<int (CliArgs const&), main::{lambda(CliArgs const&)#4}>::_M_invoke(std::_Any_data const&, CliArgs const&) [33]
|
||||||
|
0.00 0.00 9/9 CliCommand::~CliCommand() [36]
|
||||||
|
0.00 0.00 7/19 std::_Function_handler<int (CliArgs const&), int (*)(CliArgs const&)>::_M_manager(std::_Any_data&, std::_Any_data const&, std::_Manager_operation) [34]
|
||||||
|
0.00 0.00 7/7 std::vector<CliCommand, std::allocator<CliCommand> >::push_back(CliCommand const&) [37]
|
||||||
|
0.00 0.00 4/21 void std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::_M_construct<true>(char const*, unsigned long) [32]
|
||||||
|
0.00 0.00 1/1 parseArgs(int, char**) [45]
|
||||||
|
0.00 0.00 1/11 bool std::operator==<char, std::char_traits<char>, std::allocator<char> >(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, char const*) [35]
|
||||||
|
0.00 0.00 1/1 std::_Function_handler<int (CliArgs const&), int (*)(CliArgs const&)>::_M_invoke(std::_Any_data const&, CliArgs const&) [49]
|
||||||
|
0.00 0.00 1/3 std::_Function_handler<int (CliArgs const&), main::{lambda(CliArgs const&)#4}>::_M_manager(std::_Any_data&, std::_Any_data const&, std::_Manager_operation) [40]
|
||||||
|
0.00 0.00 1/3 std::_Function_handler<int (CliArgs const&), main::{lambda(CliArgs const&)#3}>::_M_manager(std::_Any_data&, std::_Any_data const&, std::_Manager_operation) [39]
|
||||||
|
0.00 0.00 1/3 std::_Function_handler<int (CliArgs const&), main::{lambda(CliArgs const&)#2}>::_M_manager(std::_Any_data&, std::_Any_data const&, std::_Manager_operation) [38]
|
||||||
|
0.00 0.00 1/3 std::_Function_handler<int (CliArgs const&), main::{lambda(CliArgs const&)#1}>::_M_manager(std::_Any_data&, std::_Any_data const&, std::_Manager_operation) [41]
|
||||||
|
-----------------------------------------------
|
||||||
|
0.00 0.30 1/1 main [1]
|
||||||
|
[2] 93.8 0.00 0.30 1 cmdTokens(CliArgs const&) [2]
|
||||||
|
0.00 0.30 1/1 Tokenizer::scan(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >) [3]
|
||||||
|
0.00 0.00 461302/461302 DelimiterToken::~DelimiterToken() [23]
|
||||||
|
0.00 0.00 230202/230202 OperatorToken::~OperatorToken() [24]
|
||||||
|
0.00 0.00 175303/175303 IdentifierToken::~IdentifierToken() [26]
|
||||||
|
0.00 0.00 133847/133847 NumberToken::~NumberToken() [29]
|
||||||
|
0.00 0.00 58981/58981 KeywordToken::~KeywordToken() [30]
|
||||||
|
0.00 0.00 2/21 void std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::_M_construct<true>(char const*, unsigned long) [32]
|
||||||
|
0.00 0.00 1/1 readSource[abi:cxx11](CliArgs const&) [44]
|
||||||
|
-----------------------------------------------
|
||||||
|
0.00 0.30 1/1 cmdTokens(CliArgs const&) [2]
|
||||||
|
[3] 93.8 0.00 0.30 1 Tokenizer::scan(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >) [3]
|
||||||
|
0.03 0.27 1059636/1059636 Tokenizer::scope() [4]
|
||||||
|
0.00 0.00 175303/175303 Tokenizer::readIdentifier() [9]
|
||||||
|
0.00 0.00 1059635/2509314 Lexer::isEnd() [17]
|
||||||
|
0.00 0.00 1/1 Lexer::setSourceText(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) [48]
|
||||||
|
-----------------------------------------------
|
||||||
|
0.03 0.27 1059636/1059636 Tokenizer::scan(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >) [3]
|
||||||
|
[4] 93.2 0.03 0.27 1059636 Tokenizer::scope() [4]
|
||||||
|
0.26 0.00 66967977/66967977 Lexer::include(std::basic_string_view<char, std::char_traits<char> >, bool) [5]
|
||||||
|
0.00 0.01 750485/750485 Lexer::getLocation() [8]
|
||||||
|
0.00 0.00 133847/133847 Lexer::readNumeric() [10]
|
||||||
|
0.00 0.00 1500970/1851576 Lexer::getOffset() [18]
|
||||||
|
0.00 0.00 1059774/2509314 Lexer::isEnd() [17]
|
||||||
|
0.00 0.00 1059773/1449678 Lexer::getchar() [19]
|
||||||
|
0.00 0.00 1059640/1059640 Lexer::skipWhiteSpace() [20]
|
||||||
|
0.00 0.00 1059635/1059635 Lexer::isNumeric() [21]
|
||||||
|
0.00 0.00 750485/750485 Lexer::toChar(int) [22]
|
||||||
|
0.00 0.00 58981/58981 Lexer::getchar(int) [31]
|
||||||
|
0.00 0.00 138/214740 Lexer::nextChar() [25]
|
||||||
|
-----------------------------------------------
|
||||||
|
0.26 0.00 66967977/66967977 Tokenizer::scope() [4]
|
||||||
|
[5] 81.2 0.26 0.00 66967977 Lexer::include(std::basic_string_view<char, std::char_traits<char> >, bool) [5]
|
||||||
|
-----------------------------------------------
|
||||||
|
<spontaneous>
|
||||||
|
[6] 6.2 0.02 0.00 _init [6]
|
||||||
|
-----------------------------------------------
|
||||||
|
0.00 0.00 138715/1064503 Lexer::readNumeric() [10]
|
||||||
|
0.00 0.00 175303/1064503 Tokenizer::readIdentifier() [9]
|
||||||
|
0.01 0.00 750485/1064503 Lexer::getLocation() [8]
|
||||||
|
[7] 3.1 0.01 0.00 1064503 SourceFile::offsetToLocation(int) const [7]
|
||||||
|
-----------------------------------------------
|
||||||
|
0.00 0.01 750485/750485 Tokenizer::scope() [4]
|
||||||
|
[8] 2.2 0.00 0.01 750485 Lexer::getLocation() [8]
|
||||||
|
0.01 0.00 750485/1064503 SourceFile::offsetToLocation(int) const [7]
|
||||||
|
-----------------------------------------------
|
||||||
|
0.00 0.00 175303/175303 Tokenizer::scan(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >) [3]
|
||||||
|
[9] 0.5 0.00 0.00 175303 Tokenizer::readIdentifier() [9]
|
||||||
|
0.00 0.00 175303/1064503 SourceFile::offsetToLocation(int) const [7]
|
||||||
|
0.00 0.00 389905/2509314 Lexer::isEnd() [17]
|
||||||
|
0.00 0.00 389905/1449678 Lexer::getchar() [19]
|
||||||
|
0.00 0.00 350606/1851576 Lexer::getOffset() [18]
|
||||||
|
0.00 0.00 214602/214740 Lexer::nextChar() [25]
|
||||||
|
0.00 0.00 175303/175303 Lexer::beginPosition() [27]
|
||||||
|
0.00 0.00 175303/175303 Lexer::acceptPosition() [28]
|
||||||
|
-----------------------------------------------
|
||||||
|
0.00 0.00 133847/133847 Tokenizer::scope() [4]
|
||||||
|
[10] 0.4 0.00 0.00 133847 Lexer::readNumeric() [10]
|
||||||
|
0.00 0.00 138715/1064503 SourceFile::offsetToLocation(int) const [7]
|
||||||
|
-----------------------------------------------
|
||||||
|
0.00 0.00 389905/2509314 Tokenizer::readIdentifier() [9]
|
||||||
|
0.00 0.00 1059635/2509314 Tokenizer::scan(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >) [3]
|
||||||
|
0.00 0.00 1059774/2509314 Tokenizer::scope() [4]
|
||||||
|
[17] 0.0 0.00 0.00 2509314 Lexer::isEnd() [17]
|
||||||
|
-----------------------------------------------
|
||||||
|
0.00 0.00 350606/1851576 Tokenizer::readIdentifier() [9]
|
||||||
|
0.00 0.00 1500970/1851576 Tokenizer::scope() [4]
|
||||||
|
[18] 0.0 0.00 0.00 1851576 Lexer::getOffset() [18]
|
||||||
|
-----------------------------------------------
|
||||||
|
0.00 0.00 389905/1449678 Tokenizer::readIdentifier() [9]
|
||||||
|
0.00 0.00 1059773/1449678 Tokenizer::scope() [4]
|
||||||
|
[19] 0.0 0.00 0.00 1449678 Lexer::getchar() [19]
|
||||||
|
-----------------------------------------------
|
||||||
|
0.00 0.00 1059640/1059640 Tokenizer::scope() [4]
|
||||||
|
[20] 0.0 0.00 0.00 1059640 Lexer::skipWhiteSpace() [20]
|
||||||
|
-----------------------------------------------
|
||||||
|
0.00 0.00 1059635/1059635 Tokenizer::scope() [4]
|
||||||
|
[21] 0.0 0.00 0.00 1059635 Lexer::isNumeric() [21]
|
||||||
|
-----------------------------------------------
|
||||||
|
0.00 0.00 750485/750485 Tokenizer::scope() [4]
|
||||||
|
[22] 0.0 0.00 0.00 750485 Lexer::toChar(int) [22]
|
||||||
|
-----------------------------------------------
|
||||||
|
0.00 0.00 461302/461302 cmdTokens(CliArgs const&) [2]
|
||||||
|
[23] 0.0 0.00 0.00 461302 DelimiterToken::~DelimiterToken() [23]
|
||||||
|
-----------------------------------------------
|
||||||
|
0.00 0.00 230202/230202 cmdTokens(CliArgs const&) [2]
|
||||||
|
[24] 0.0 0.00 0.00 230202 OperatorToken::~OperatorToken() [24]
|
||||||
|
-----------------------------------------------
|
||||||
|
0.00 0.00 138/214740 Tokenizer::scope() [4]
|
||||||
|
0.00 0.00 214602/214740 Tokenizer::readIdentifier() [9]
|
||||||
|
[25] 0.0 0.00 0.00 214740 Lexer::nextChar() [25]
|
||||||
|
-----------------------------------------------
|
||||||
|
0.00 0.00 175303/175303 cmdTokens(CliArgs const&) [2]
|
||||||
|
[26] 0.0 0.00 0.00 175303 IdentifierToken::~IdentifierToken() [26]
|
||||||
|
-----------------------------------------------
|
||||||
|
0.00 0.00 175303/175303 Tokenizer::readIdentifier() [9]
|
||||||
|
[27] 0.0 0.00 0.00 175303 Lexer::beginPosition() [27]
|
||||||
|
-----------------------------------------------
|
||||||
|
0.00 0.00 175303/175303 Tokenizer::readIdentifier() [9]
|
||||||
|
[28] 0.0 0.00 0.00 175303 Lexer::acceptPosition() [28]
|
||||||
|
-----------------------------------------------
|
||||||
|
0.00 0.00 133847/133847 cmdTokens(CliArgs const&) [2]
|
||||||
|
[29] 0.0 0.00 0.00 133847 NumberToken::~NumberToken() [29]
|
||||||
|
-----------------------------------------------
|
||||||
|
0.00 0.00 58981/58981 cmdTokens(CliArgs const&) [2]
|
||||||
|
[30] 0.0 0.00 0.00 58981 KeywordToken::~KeywordToken() [30]
|
||||||
|
-----------------------------------------------
|
||||||
|
0.00 0.00 58981/58981 Tokenizer::scope() [4]
|
||||||
|
[31] 0.0 0.00 0.00 58981 Lexer::getchar(int) [31]
|
||||||
|
-----------------------------------------------
|
||||||
|
0.00 0.00 1/21 readSource[abi:cxx11](CliArgs const&) [44]
|
||||||
|
0.00 0.00 2/21 cmdTokens(CliArgs const&) [2]
|
||||||
|
0.00 0.00 4/21 main [1]
|
||||||
|
0.00 0.00 14/21 std::vector<CliCommand, std::allocator<CliCommand> >::push_back(CliCommand const&) [37]
|
||||||
|
[32] 0.0 0.00 0.00 21 void std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::_M_construct<true>(char const*, unsigned long) [32]
|
||||||
|
-----------------------------------------------
|
||||||
|
0.00 0.00 2/20 parseArgs(int, char**) [45]
|
||||||
|
0.00 0.00 18/20 main [1]
|
||||||
|
[33] 0.0 0.00 0.00 20 std::_Function_handler<int (CliArgs const&), main::{lambda(CliArgs const&)#4}>::_M_invoke(std::_Any_data const&, CliArgs const&) [33]
|
||||||
|
-----------------------------------------------
|
||||||
|
0.00 0.00 3/19 std::vector<CliCommand, std::allocator<CliCommand> >::push_back(CliCommand const&) [37]
|
||||||
|
0.00 0.00 4/19 parseArgs(int, char**) [45]
|
||||||
|
0.00 0.00 5/19 CliCommand::~CliCommand() [36]
|
||||||
|
0.00 0.00 7/19 main [1]
|
||||||
|
[34] 0.0 0.00 0.00 19 std::_Function_handler<int (CliArgs const&), int (*)(CliArgs const&)>::_M_manager(std::_Any_data&, std::_Any_data const&, std::_Manager_operation) [34]
|
||||||
|
-----------------------------------------------
|
||||||
|
0.00 0.00 1/11 main [1]
|
||||||
|
0.00 0.00 10/11 parseArgs(int, char**) [45]
|
||||||
|
[35] 0.0 0.00 0.00 11 bool std::operator==<char, std::char_traits<char>, std::allocator<char> >(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, char const*) [35]
|
||||||
|
-----------------------------------------------
|
||||||
|
0.00 0.00 9/9 main [1]
|
||||||
|
[36] 0.0 0.00 0.00 9 CliCommand::~CliCommand() [36]
|
||||||
|
0.00 0.00 5/19 std::_Function_handler<int (CliArgs const&), int (*)(CliArgs const&)>::_M_manager(std::_Any_data&, std::_Any_data const&, std::_Manager_operation) [34]
|
||||||
|
0.00 0.00 1/3 std::_Function_handler<int (CliArgs const&), main::{lambda(CliArgs const&)#4}>::_M_manager(std::_Any_data&, std::_Any_data const&, std::_Manager_operation) [40]
|
||||||
|
0.00 0.00 1/3 std::_Function_handler<int (CliArgs const&), main::{lambda(CliArgs const&)#3}>::_M_manager(std::_Any_data&, std::_Any_data const&, std::_Manager_operation) [39]
|
||||||
|
0.00 0.00 1/3 std::_Function_handler<int (CliArgs const&), main::{lambda(CliArgs const&)#2}>::_M_manager(std::_Any_data&, std::_Any_data const&, std::_Manager_operation) [38]
|
||||||
|
0.00 0.00 1/3 std::_Function_handler<int (CliArgs const&), main::{lambda(CliArgs const&)#1}>::_M_manager(std::_Any_data&, std::_Any_data const&, std::_Manager_operation) [41]
|
||||||
|
-----------------------------------------------
|
||||||
|
0.00 0.00 7/7 main [1]
|
||||||
|
[37] 0.0 0.00 0.00 7 std::vector<CliCommand, std::allocator<CliCommand> >::push_back(CliCommand const&) [37]
|
||||||
|
0.00 0.00 14/21 void std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::_M_construct<true>(char const*, unsigned long) [32]
|
||||||
|
0.00 0.00 3/19 std::_Function_handler<int (CliArgs const&), int (*)(CliArgs const&)>::_M_manager(std::_Any_data&, std::_Any_data const&, std::_Manager_operation) [34]
|
||||||
|
0.00 0.00 1/3 std::_Function_handler<int (CliArgs const&), main::{lambda(CliArgs const&)#3}>::_M_manager(std::_Any_data&, std::_Any_data const&, std::_Manager_operation) [39]
|
||||||
|
0.00 0.00 1/3 std::_Function_handler<int (CliArgs const&), main::{lambda(CliArgs const&)#2}>::_M_manager(std::_Any_data&, std::_Any_data const&, std::_Manager_operation) [38]
|
||||||
|
0.00 0.00 1/3 std::_Function_handler<int (CliArgs const&), main::{lambda(CliArgs const&)#1}>::_M_manager(std::_Any_data&, std::_Any_data const&, std::_Manager_operation) [41]
|
||||||
|
0.00 0.00 1/3 std::_Function_handler<int (CliArgs const&), main::{lambda(CliArgs const&)#4}>::_M_manager(std::_Any_data&, std::_Any_data const&, std::_Manager_operation) [40]
|
||||||
|
-----------------------------------------------
|
||||||
|
0.00 0.00 1/3 main [1]
|
||||||
|
0.00 0.00 1/3 CliCommand::~CliCommand() [36]
|
||||||
|
0.00 0.00 1/3 std::vector<CliCommand, std::allocator<CliCommand> >::push_back(CliCommand const&) [37]
|
||||||
|
[38] 0.0 0.00 0.00 3 std::_Function_handler<int (CliArgs const&), main::{lambda(CliArgs const&)#2}>::_M_manager(std::_Any_data&, std::_Any_data const&, std::_Manager_operation) [38]
|
||||||
|
-----------------------------------------------
|
||||||
|
0.00 0.00 1/3 main [1]
|
||||||
|
0.00 0.00 1/3 CliCommand::~CliCommand() [36]
|
||||||
|
0.00 0.00 1/3 std::vector<CliCommand, std::allocator<CliCommand> >::push_back(CliCommand const&) [37]
|
||||||
|
[39] 0.0 0.00 0.00 3 std::_Function_handler<int (CliArgs const&), main::{lambda(CliArgs const&)#3}>::_M_manager(std::_Any_data&, std::_Any_data const&, std::_Manager_operation) [39]
|
||||||
|
-----------------------------------------------
|
||||||
|
0.00 0.00 1/3 main [1]
|
||||||
|
0.00 0.00 1/3 CliCommand::~CliCommand() [36]
|
||||||
|
0.00 0.00 1/3 std::vector<CliCommand, std::allocator<CliCommand> >::push_back(CliCommand const&) [37]
|
||||||
|
[40] 0.0 0.00 0.00 3 std::_Function_handler<int (CliArgs const&), main::{lambda(CliArgs const&)#4}>::_M_manager(std::_Any_data&, std::_Any_data const&, std::_Manager_operation) [40]
|
||||||
|
-----------------------------------------------
|
||||||
|
0.00 0.00 1/3 main [1]
|
||||||
|
0.00 0.00 1/3 CliCommand::~CliCommand() [36]
|
||||||
|
0.00 0.00 1/3 std::vector<CliCommand, std::allocator<CliCommand> >::push_back(CliCommand const&) [37]
|
||||||
|
[41] 0.0 0.00 0.00 3 std::_Function_handler<int (CliArgs const&), main::{lambda(CliArgs const&)#1}>::_M_manager(std::_Any_data&, std::_Any_data const&, std::_Manager_operation) [41]
|
||||||
|
-----------------------------------------------
|
||||||
|
0.00 0.00 2/2 __static_initialization_and_destruction_0() [66]
|
||||||
|
[42] 0.0 0.00 0.00 2 std::_Hashtable<TokenType, std::pair<TokenType const, std::basic_string_view<char, std::char_traits<char> > >, std::allocator<std::pair<TokenType const, std::basic_string_view<char, std::char_traits<char> > > >, std::__detail::_Select1st, std::equal_to<TokenType>, std::hash<TokenType>, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, std::__detail::_Hashtable_traits<false, false, true> >::_Hashtable<std::pair<TokenType const, std::basic_string_view<char, std::char_traits<char> > > const*>(std::pair<TokenType const, std::basic_string_view<char, std::char_traits<char> > > const*, std::pair<TokenType const, std::basic_string_view<char, std::char_traits<char> > > const*, unsigned long, std::hash<TokenType> const&, std::equal_to<TokenType> const&, std::allocator<std::pair<TokenType const, std::basic_string_view<char, std::char_traits<char> > > > const&, std::integral_constant<bool, true>) [42]
|
||||||
|
-----------------------------------------------
|
||||||
|
0.00 0.00 2/2 __static_initialization_and_destruction_0() [66]
|
||||||
|
[43] 0.0 0.00 0.00 2 std::_Hashtable<std::basic_string_view<char, std::char_traits<char> >, std::pair<std::basic_string_view<char, std::char_traits<char> > const, TokenType>, std::allocator<std::pair<std::basic_string_view<char, std::char_traits<char> > const, TokenType> >, std::__detail::_Select1st, std::equal_to<std::basic_string_view<char, std::char_traits<char> > >, std::hash<std::basic_string_view<char, std::char_traits<char> > >, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, std::__detail::_Hashtable_traits<true, false, true> >::_Hashtable<std::pair<std::basic_string_view<char, std::char_traits<char> > const, TokenType> const*>(std::pair<std::basic_string_view<char, std::char_traits<char> > const, TokenType> const*, std::pair<std::basic_string_view<char, std::char_traits<char> > const, TokenType> const*, unsigned long, std::hash<std::basic_string_view<char, std::char_traits<char> > > const&, std::equal_to<std::basic_string_view<char, std::char_traits<char> > > const&, std::allocator<std::pair<std::basic_string_view<char, std::char_traits<char> > const, TokenType> > const&, std::integral_constant<bool, true>) [43]
|
||||||
|
-----------------------------------------------
|
||||||
|
0.00 0.00 1/1 cmdTokens(CliArgs const&) [2]
|
||||||
|
[44] 0.0 0.00 0.00 1 readSource[abi:cxx11](CliArgs const&) [44]
|
||||||
|
0.00 0.00 1/21 void std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::_M_construct<true>(char const*, unsigned long) [32]
|
||||||
|
-----------------------------------------------
|
||||||
|
0.00 0.00 1/1 main [1]
|
||||||
|
[45] 0.0 0.00 0.00 1 parseArgs(int, char**) [45]
|
||||||
|
0.00 0.00 10/11 bool std::operator==<char, std::char_traits<char>, std::allocator<char> >(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, char const*) [35]
|
||||||
|
0.00 0.00 4/19 std::_Function_handler<int (CliArgs const&), int (*)(CliArgs const&)>::_M_manager(std::_Any_data&, std::_Any_data const&, std::_Manager_operation) [34]
|
||||||
|
0.00 0.00 2/20 std::_Function_handler<int (CliArgs const&), main::{lambda(CliArgs const&)#4}>::_M_invoke(std::_Any_data const&, CliArgs const&) [33]
|
||||||
|
-----------------------------------------------
|
||||||
|
0.00 0.00 1/1 Lexer::setSourceText(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) [48]
|
||||||
|
[46] 0.0 0.00 0.00 1 SourceFile::computeLineStarts() [46]
|
||||||
|
-----------------------------------------------
|
||||||
|
0.00 0.00 1/1 Lexer::setSourceText(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) [48]
|
||||||
|
[47] 0.0 0.00 0.00 1 SourceFile::setText(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) [47]
|
||||||
|
-----------------------------------------------
|
||||||
|
0.00 0.00 1/1 Tokenizer::scan(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >) [3]
|
||||||
|
[48] 0.0 0.00 0.00 1 Lexer::setSourceText(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) [48]
|
||||||
|
0.00 0.00 1/1 SourceFile::computeLineStarts() [46]
|
||||||
|
0.00 0.00 1/1 SourceFile::setText(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) [47]
|
||||||
|
-----------------------------------------------
|
||||||
|
0.00 0.00 1/1 main [1]
|
||||||
|
[49] 0.0 0.00 0.00 1 std::_Function_handler<int (CliArgs const&), int (*)(CliArgs const&)>::_M_invoke(std::_Any_data const&, CliArgs const&) [49]
|
||||||
|
-----------------------------------------------
|
||||||
|
|
||||||
|
This table describes the call tree of the program, and was sorted by
|
||||||
|
the total amount of time spent in each function and its children.
|
||||||
|
|
||||||
|
Each entry in this table consists of several lines. The line with the
|
||||||
|
index number at the left hand margin lists the current function.
|
||||||
|
The lines above it list the functions that called this function,
|
||||||
|
and the lines below it list the functions this one called.
|
||||||
|
This line lists:
|
||||||
|
index A unique number given to each element of the table.
|
||||||
|
Index numbers are sorted numerically.
|
||||||
|
The index number is printed next to every function name so
|
||||||
|
it is easier to look up where the function is in the table.
|
||||||
|
|
||||||
|
% time This is the percentage of the `total' time that was spent
|
||||||
|
in this function and its children. Note that due to
|
||||||
|
different viewpoints, functions excluded by options, etc,
|
||||||
|
these numbers will NOT add up to 100%.
|
||||||
|
|
||||||
|
self This is the total amount of time spent in this function.
|
||||||
|
|
||||||
|
children This is the total amount of time propagated into this
|
||||||
|
function by its children.
|
||||||
|
|
||||||
|
called This is the number of times the function was called.
|
||||||
|
If the function called itself recursively, the number
|
||||||
|
only includes non-recursive calls, and is followed by
|
||||||
|
a `+' and the number of recursive calls.
|
||||||
|
|
||||||
|
name The name of the current function. The index number is
|
||||||
|
printed after it. If the function is a member of a
|
||||||
|
cycle, the cycle number is printed between the
|
||||||
|
function's name and the index number.
|
||||||
|
|
||||||
|
|
||||||
|
For the function's parents, the fields have the following meanings:
|
||||||
|
|
||||||
|
self This is the amount of time that was propagated directly
|
||||||
|
from the function into this parent.
|
||||||
|
|
||||||
|
children This is the amount of time that was propagated from
|
||||||
|
the function's children into this parent.
|
||||||
|
|
||||||
|
called This is the number of times this parent called the
|
||||||
|
function `/' the total number of times the function
|
||||||
|
was called. Recursive calls to the function are not
|
||||||
|
included in the number after the `/'.
|
||||||
|
|
||||||
|
name This is the name of the parent. The parent's index
|
||||||
|
number is printed after it. If the parent is a
|
||||||
|
member of a cycle, the cycle number is printed between
|
||||||
|
the name and the index number.
|
||||||
|
|
||||||
|
If the parents of the function cannot be determined, the word
|
||||||
|
`<spontaneous>' is printed in the `name' field, and all the other
|
||||||
|
fields are blank.
|
||||||
|
|
||||||
|
For the function's children, the fields have the following meanings:
|
||||||
|
|
||||||
|
self This is the amount of time that was propagated directly
|
||||||
|
from the child into the function.
|
||||||
|
|
||||||
|
children This is the amount of time that was propagated from the
|
||||||
|
child's children to the function.
|
||||||
|
|
||||||
|
called This is the number of times the function called
|
||||||
|
this child `/' the total number of times the child
|
||||||
|
was called. Recursive calls by the child are not
|
||||||
|
listed in the number after the `/'.
|
||||||
|
|
||||||
|
name This is the name of the child. The child's index
|
||||||
|
number is printed after it. If the child is a
|
||||||
|
member of a cycle, the cycle number is printed
|
||||||
|
between the name and the index number.
|
||||||
|
|
||||||
|
If there are any cycles (circles) in the call graph, there is an
|
||||||
|
entry for the cycle-as-a-whole. This entry shows who called the
|
||||||
|
cycle (as parents) and the members of the cycle (as children.)
|
||||||
|
The `+' recursive calls entry shows the number of function calls that
|
||||||
|
were internal to the cycle, and the calls entry for each member shows,
|
||||||
|
for that member, how many times it was called from other members of
|
||||||
|
the cycle.
|
||||||
|
|
||||||
|
Copyright (C) 2012-2026 Free Software Foundation, Inc.
|
||||||
|
|
||||||
|
Copying and distribution of this file, with or without modification,
|
||||||
|
are permitted in any medium without royalty provided the copyright
|
||||||
|
notice and this notice are preserved.
|
||||||
|
|
||||||
|
Index by function name
|
||||||
|
|
||||||
|
[44] readSource[abi:cxx11](CliArgs const&) [48] Lexer::setSourceText(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) [7] SourceFile::offsetToLocation(int) const
|
||||||
|
[2] cmdTokens(CliArgs const&) [28] Lexer::acceptPosition() [42] std::_Hashtable<TokenType, std::pair<TokenType const, std::basic_string_view<char, std::char_traits<char> > >, std::allocator<std::pair<TokenType const, std::basic_string_view<char, std::char_traits<char> > > >, std::__detail::_Select1st, std::equal_to<TokenType>, std::hash<TokenType>, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, std::__detail::_Hashtable_traits<false, false, true> >::_Hashtable<std::pair<TokenType const, std::basic_string_view<char, std::char_traits<char> > > const*>(std::pair<TokenType const, std::basic_string_view<char, std::char_traits<char> > > const*, std::pair<TokenType const, std::basic_string_view<char, std::char_traits<char> > > const*, unsigned long, std::hash<TokenType> const&, std::equal_to<TokenType> const&, std::allocator<std::pair<TokenType const, std::basic_string_view<char, std::char_traits<char> > > > const&, std::integral_constant<bool, true>)
|
||||||
|
[45] parseArgs(int, char**) [20] Lexer::skipWhiteSpace() [43] std::_Hashtable<std::basic_string_view<char, std::char_traits<char> >, std::pair<std::basic_string_view<char, std::char_traits<char> > const, TokenType>, std::allocator<std::pair<std::basic_string_view<char, std::char_traits<char> > const, TokenType> >, std::__detail::_Select1st, std::equal_to<std::basic_string_view<char, std::char_traits<char> > >, std::hash<std::basic_string_view<char, std::char_traits<char> > >, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, std::__detail::_Hashtable_traits<true, false, true> >::_Hashtable<std::pair<std::basic_string_view<char, std::char_traits<char> > const, TokenType> const*>(std::pair<std::basic_string_view<char, std::char_traits<char> > const, TokenType> const*, std::pair<std::basic_string_view<char, std::char_traits<char> > const, TokenType> const*, unsigned long, std::hash<std::basic_string_view<char, std::char_traits<char> > > const&, std::equal_to<std::basic_string_view<char, std::char_traits<char> > > const&, std::allocator<std::pair<std::basic_string_view<char, std::char_traits<char> > const, TokenType> > const&, std::integral_constant<bool, true>)
|
||||||
|
[36] CliCommand::~CliCommand() [17] Lexer::isEnd() [34] std::_Function_handler<int (CliArgs const&), int (*)(CliArgs const&)>::_M_manager(std::_Any_data&, std::_Any_data const&, std::_Manager_operation)
|
||||||
|
[46] SourceFile::computeLineStarts() [22] Lexer::toChar(int) [49] std::_Function_handler<int (CliArgs const&), int (*)(CliArgs const&)>::_M_invoke(std::_Any_data const&, CliArgs const&)
|
||||||
|
[47] SourceFile::setText(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) [31] Lexer::getchar(int) [38] std::_Function_handler<int (CliArgs const&), main::{lambda(CliArgs const&)#2}>::_M_manager(std::_Any_data&, std::_Any_data const&, std::_Manager_operation) (std_function.h)
|
||||||
|
[29] NumberToken::~NumberToken() [19] Lexer::getchar() [39] std::_Function_handler<int (CliArgs const&), main::{lambda(CliArgs const&)#3}>::_M_manager(std::_Any_data&, std::_Any_data const&, std::_Manager_operation) (std_function.h)
|
||||||
|
[30] KeywordToken::~KeywordToken() [5] Lexer::include(std::basic_string_view<char, std::char_traits<char> >, bool) [40] std::_Function_handler<int (CliArgs const&), main::{lambda(CliArgs const&)#4}>::_M_manager(std::_Any_data&, std::_Any_data const&, std::_Manager_operation) (std_function.h)
|
||||||
|
[24] OperatorToken::~OperatorToken() [25] Lexer::nextChar() [33] std::_Function_handler<int (CliArgs const&), main::{lambda(CliArgs const&)#4}>::_M_invoke(std::_Any_data const&, CliArgs const&) (std_function.h)
|
||||||
|
[23] DelimiterToken::~DelimiterToken() [18] Lexer::getOffset() [41] std::_Function_handler<int (CliArgs const&), main::{lambda(CliArgs const&)#1}>::_M_manager(std::_Any_data&, std::_Any_data const&, std::_Manager_operation) (std_function.h)
|
||||||
|
[26] IdentifierToken::~IdentifierToken() [21] Lexer::isNumeric() [37] std::vector<CliCommand, std::allocator<CliCommand> >::push_back(CliCommand const&)
|
||||||
|
[8] Lexer::getLocation() [9] Tokenizer::readIdentifier() [32] void std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::_M_construct<true>(char const*, unsigned long)
|
||||||
|
[10] Lexer::readNumeric() [3] Tokenizer::scan(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >) [35] bool std::operator==<char, std::char_traits<char>, std::allocator<char> >(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, char const*)
|
||||||
|
[27] Lexer::beginPosition() [4] Tokenizer::scope() [6] _init
|
||||||
|
|
@ -0,0 +1,30 @@
|
||||||
|
# PR Açıklaması: Parser ve AST Bileşenlerinin Modülerleştirilmesi
|
||||||
|
|
||||||
|
## Açıklama
|
||||||
|
Bu PR, `src/parser/` dizini altındaki devasa `ast.hpp` ve `parser.hpp` dosyalarını mantıksal parçalara ayırarak kodun okunabilirliğini ve bakımını kolaylaştırmayı amaçlar. Kodun işlevselliği korunmuş, sadece dosya yapısı modüler hale getirilmiştir.
|
||||||
|
|
||||||
|
## Önemli Değişiklikler
|
||||||
|
|
||||||
|
### 1. AST (Soyut Sözdizim Ağacı) Modülerleştirme
|
||||||
|
`ast.hpp` dosyası artık bir **aggregator** (toplayıcı) görevi görüyor ve aşağıdaki yeni dosyalardan bileşenleri dahil ediyor:
|
||||||
|
- `ast_node.hpp`: Temel `ASTNode` sınıfı ve `ASTKind` enum'u.
|
||||||
|
- `ast_expr.hpp`: `LiteralNode`, `BinaryExpressionNode`, `CallExpressionNode` gibi ifade düğümleri.
|
||||||
|
- `ast_stmt.hpp`: `BlockNode`, `IfStatementNode`, `WhileStatementNode` gibi deyim (statement) düğümleri.
|
||||||
|
- `ast_decl.hpp`: `FunctionDeclNode`, `VariableDeclNode`, `StructDeclNode` gibi deklarasyon düğümleri.
|
||||||
|
- `ast_json.hpp`: JSON serileştirme için yardımcı fonksiyonlar (`childrenToJson`, `jsonEscape` vb.).
|
||||||
|
|
||||||
|
### 2. Parser Modülerleştirme
|
||||||
|
Parser mantığı da benzer şekilde parçalara ayrıldı:
|
||||||
|
- `parser_base.hpp`: `Parser` sınıfı tanımı ve üye değişkenleri.
|
||||||
|
- `parser_core.hpp`: Pratt Parser ana döngüsü, NUD ve LED mantığı.
|
||||||
|
- `parser_decl.hpp`: Fonksiyon, değişken ve struct deklarasyonlarının ayrıştırılması.
|
||||||
|
- `parser_stmt.hpp`: Deyimlerin (if, for, while, return vb.) ayrıştırılması.
|
||||||
|
|
||||||
|
## Teknik Avantajlar
|
||||||
|
- **Okunabilirlik:** Binlerce satırlık dosyalar yerine 100-300 satırlık, spesifik görevleri olan dosyalar oluşturuldu.
|
||||||
|
- **Bakım Kolaylığı:** Belirli bir dil özelliği (örn. yeni bir deyim tipi) eklendiğinde hangi dosyanın değiştirileceği artık çok daha net.
|
||||||
|
- **Derleme Hızı:** (Gelecekte) İncremental build süreçlerinde sadece değişen parçaların derlenmesine olanak sağlar.
|
||||||
|
|
||||||
|
## Notlar
|
||||||
|
- Mevcut tüm testler ve `Final.sqt` gibi örnek dosyaların ayrıştırılması sorunsuz çalışmaya devam etmektedir.
|
||||||
|
- Dosya başlıklarındaki DİZİN ve KATMAN bilgileri güncellenmiştir.
|
||||||
|
|
@ -0,0 +1,226 @@
|
||||||
|
# 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…019).
|
||||||
|
> Tartışma akışı: `docs/transkript-frontend-tasarim.md`.
|
||||||
|
>
|
||||||
|
> **İlke:** Sıralama katıdır — her faz bir öncekine dayanır. Her faz sonunda
|
||||||
|
> geçerli örnekle (`examples/fibonacci.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).
|
||||||
|
>
|
||||||
|
> ⚠️ **Yapılan vs planlanan:** Bugün çalışan = lexer, tokenizer, Pratt parser,
|
||||||
|
> AST, AST'nin JSON serileştirmesi, CLI iskeleti, konum takibi, basit aritmetiği
|
||||||
|
> düşüren minimal IR deneyi. Bu yol haritasındaki **her şey planlıdır** (sembol
|
||||||
|
> tablosu, semantik analiz, tip sistemi, diagnostic, optimizasyon).
|
||||||
|
>
|
||||||
|
> 🎯 **Bu haftanın işi:** **sembol tablosu + iki-geçişli toplayıcı** (Faz 2),
|
||||||
|
> hedef **"fibonacci'yi derle ve çalıştır"** (`examples/fibonacci.sqt`). Faz 0–1
|
||||||
|
> bunun önkoşuludur.
|
||||||
|
>
|
||||||
|
> 🧭 **Önce dikey dilim, sonra çerçeve.** Bir şey çalışmadan önce genel pass
|
||||||
|
> manager / evrensel config / ağır soyutlama inşa etme. Uçtan uca tek bir dilim
|
||||||
|
> (kaynak → IR → çalıştır; tamsayı + değişken + kontrol akışı + tek `print`)
|
||||||
|
> önce çalışsın. Faz 4'ün framework'ü (OptimizationManager, fixpoint, config)
|
||||||
|
> ancak Faz 0–3 fibonacci'yi geçirdikten **sonra** anlam kazanır — erken
|
||||||
|
> soyutlama daha az değil, daha çok karmaşıklıktır.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 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 (birincil: IR + bytecode VM, ADR-015;
|
||||||
|
ileride: C transpile; makine kodu uzak gelecek — "JIT" kapsam dışı)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 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 (declare-before-use ihlali dahil — lokal ve **global başlatıcı**, ADR-011) | Faz 2/3 |
|
||||||
|
| `E002` | Aynı scope'ta çift tanım | Faz 2 |
|
||||||
|
| `E003` | Tip uyuşmazlığı (gizli dönüşüm yok; literal için bağlama-göre kural, ADR-010) | 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 |
|
||||||
|
| `E010` | **Özyinelemeli/döngüsel struct tanımı** (by-value çevrim → sonsuz boyut, ADR-011) | Faz 2/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.)*
|
||||||
|
|
||||||
|
**Literal / başlatıcı kuralları (ADR-010/011) hata kataloğunu nasıl etkiler:**
|
||||||
|
- `float x = 1;` → **hata değil** (tamsayı literali bağlama-göre tiplenir,
|
||||||
|
kayıpsız). `int y = 1.5;` → `E003` (kayıp). `float x = anInt;` → `E003`
|
||||||
|
(değişken→değişken gizli dönüşüm yok).
|
||||||
|
- `int a = b; int b = 5;` (global) → `E001` (global başlatıcı declare-before-use).
|
||||||
|
|
||||||
|
### 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/fibonacci.sqt` hâlâ geçerli JSON (regresyon yok,
|
||||||
|
`python3 -m json.tool` ile). Parser regresyonu için ayrıca
|
||||||
|
`examples/parser-stress/Final.sqt` de geçerli JSON üretmeye devam etmeli (bu
|
||||||
|
dosya **geçerli program değildir**, yalnızca parser/AST stres fixture'ıdır —
|
||||||
|
semantik fazlarda fixture olarak kullanılmaz).
|
||||||
|
- 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`; çift tanım → `E002`.
|
||||||
|
- **Döngüsel struct kontrolü (`E010`):** Geçiş 1'den sonra struct'lar düğüm,
|
||||||
|
"alanı olarak içerir" kenarıyla bir çevrim-arama (DFS) çalıştır; çevrim →
|
||||||
|
`E010` (ADR-011). Pointer olmadığı için tüm kapsama by-value'dur; çevrim =
|
||||||
|
sonsuz boyut.
|
||||||
|
- **Global başlatıcı declare-before-use (ADR-011):** fonksiyon/struct tam hoist
|
||||||
|
edilir, global değişken **ismi** hoist edilir, ama global değişken
|
||||||
|
**başlatıcısı** kendinden önce tanımlı isimleri kullanabilir; aksi → `E001`.
|
||||||
|
|
||||||
|
### Doğrulama
|
||||||
|
- `saqut symbols examples/fibonacci.sqt` → zengin tablo (her sembolün tipi, tanım
|
||||||
|
yeri, referansları). Forward reference çalışır (sonra tanımlı fonksiyon
|
||||||
|
çağrılabilir — `main`, `fibonacci`'yi çağırır).
|
||||||
|
- Hatalı örnekler → `E001`/`E002`/`E007`/`E010` 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 **değişken→değişken** dönüşüm yok (ADR-010) → uyuşmazlıkta `E003`. **Tamsayı literali bağlama-göre tiplenir** (kayıpsızsa beklenen tipe uyar; `float x = 1;` geçerli, `int y = 1.5;` → `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 + **sert iterasyon tavanı** `maxFixpointRounds`, ADR-009). **Sonlanma değişmezi:** havuzdaki pass'ler monoton (yalnızca küçültür); büyüten pass (inlining) eklenirse tavan zorunlu. Çalışmadan önce AST'yi **klonlar** — `ASTNode::clone()` **merkezi bir bileşendir** (ADR-007): parent pointer'lar yeniden bağlanır, sembol tablosu klonlanıp `IdentifierNode→Symbol` bağları **remap** edilir. **Her turda**, klon üzerinde akışa-bağlı analiz (`isReachable`, ref-count) **yeniden hesaplanır** (ADR-009); aksi halde zincirleme fırsatlar bayat veriyle kaçar. |
|
||||||
|
| `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ı + **FFI seam** `callhost`, ADR-016) → **bytecode VM ile
|
||||||
|
çalıştırma** (ADR-015) → hedef: **`examples/fibonacci.sqt` derlenir ve çalışır.**
|
||||||
|
|
||||||
|
- **Çalıştırma modeli IR + bytecode VM'dir** (ADR-015). **Makine-kodu JIT
|
||||||
|
açıkça kapsam dışıdır;** öncelik determinizm + incelenebilirlik, ham hız değil.
|
||||||
|
- **C transpile**, ileride geçerli bir **ikinci** backend olarak kalır (frontend
|
||||||
|
backend-bağımsız). Makine kodu gerçekten istenirse libgccjit/LLVM'e bağlanılır
|
||||||
|
— çok uzak gelecek.
|
||||||
|
- IR/VM tasarlanırken **FFI seam'i şimdiden bırak** (ADR-016); `print` ilk
|
||||||
|
müşteridir. Bellek host (C++) heap'idir; özel allocator yok. Dinamik array'in
|
||||||
|
runtime modeli (ADR-014) bu fazda netleşir.
|
||||||
|
|
||||||
|
> Çerçeve uyarısı: bu sonraki adım da **önce dikey dilim** ilkesine tabidir —
|
||||||
|
> genel VM/optimizasyon altyapısı kurmadan önce fibonacci uçtan uca çalışsın.
|
||||||
|
|
@ -0,0 +1,404 @@
|
||||||
|
# saQut Compiler Toolbox — Yol Haritası ve Yapılacaklar
|
||||||
|
|
||||||
|
> **Felsefe**: saQut bir derleyici değil, bir **derleyici alet çantasıdır**.
|
||||||
|
> Amacı en hızlı kodu üretmek, en güvenli sandbox'u kurmak veya en optimize
|
||||||
|
> binary'yi çıkarmak değildir. Amacı, **derleyicinin kendisidir**.
|
||||||
|
>
|
||||||
|
> Her aşama (Lexer, Parser, Optimizer, Compiler) birbirinden bağımsız,
|
||||||
|
> kendi parametreleriyle yönlendirilebilen, gerektiğinde devre dışı
|
||||||
|
> bırakılabilen birer **araç kutusu modülü** olarak tasarlanır.
|
||||||
|
>
|
||||||
|
> Geliştirici, isterse sadece token listesini alır, isterse AST'yi JSON
|
||||||
|
> olarak çeker, isterse sembol tablosunu inceler, isterse kodu çalıştırır.
|
||||||
|
> Bu, ileride LSP sunucusu yazmayı, IDE eklentileri geliştirmeyi ve
|
||||||
|
> kod hiyerarşisi görselleştirmeyi mümkün kılar.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Temel Prensipler
|
||||||
|
|
||||||
|
1. **Her aşama bağımsız**: Lexer → Tokenizer → Parser → Optimizer → IR → Backend.
|
||||||
|
Her biri tek başına çalışabilir, kendi parametrelerini alır.
|
||||||
|
|
||||||
|
2. **Her şey metadata**: Her token ve AST düğümü; dosya adı, satır, sütun,
|
||||||
|
karakter offset'i taşır. Debug ve hata mesajları bu veri üzerine kurulur.
|
||||||
|
|
||||||
|
3. **AST bellek canavarı**: Token'lar AST'de yaşar. Kaynak kodun neredeyse
|
||||||
|
birebir izdüşümü AST üzerinde korunur. Hiçbir bilgi atılmaz.
|
||||||
|
|
||||||
|
4. **Sembol tablosu aynı felsefede**: Basit yapı, çok veri. Her sembolün
|
||||||
|
tanımlandığı yer, tipi, kullanıldığı tüm noktalar, scope bilgisi.
|
||||||
|
|
||||||
|
5. **Feature toggle**: Her keyword, her operatör, her optimizasyon adımı
|
||||||
|
açılıp kapatılabilir. `--disable-while`, `--skip-constant-folding`.
|
||||||
|
|
||||||
|
6. **Çoklu çıktı formatı**: Düz metin, JSON, (ileride) HTML/SVG. Tüm ara
|
||||||
|
gösterimler dışa aktarılabilir.
|
||||||
|
|
||||||
|
7. **IR merkezli backend**: Tüm backend'ler (C transpile, QBE, LLVM, JIT)
|
||||||
|
aynı IR'den beslenir. Yeni backend eklemek = yeni bir IR yürüteci yazmak.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Aşama 0: Metadata ve Konum Takibi
|
||||||
|
|
||||||
|
> **Hedef**: Her token ve AST düğümü nereden geldiğini bilsin.
|
||||||
|
|
||||||
|
### 0.1 SourceFile Yöneticisi
|
||||||
|
- [ ] `SourceFile` sınıfı: dosya adı, tam metin, satır başı offset'leri
|
||||||
|
- [ ] `SourceLocation` yapısı: `{file, line, column, offset}`
|
||||||
|
- [ ] `offset → (line, column)` dönüşümü (binary search ile O(log n))
|
||||||
|
- [ ] Çoklu dosya desteği (import/include için temel)
|
||||||
|
|
||||||
|
### 0.2 Lexer'a Konum Ekleme
|
||||||
|
- [ ] Lexer her `nextChar()`'da satır/sütun takibi yapsın
|
||||||
|
- [ ] `getLocation()` → mevcut konumu `SourceLocation` olarak döndürsün
|
||||||
|
- [ ] `INumber` yapısına `SourceLocation startLoc, endLoc` eklensin
|
||||||
|
|
||||||
|
### 0.3 Token'lara Konum Ekleme
|
||||||
|
- [ ] `Token` temel sınıfına `SourceLocation loc` eklensin
|
||||||
|
- [ ] Her token oluşturulurken konumu kaydedilsin
|
||||||
|
- [ ] `start`/`end` offset'leri `SourceLocation` ile değiştirilsin
|
||||||
|
|
||||||
|
### 0.4 AST Düğümlerine Konum Ekleme
|
||||||
|
- [ ] `ASTNode` temel sınıfına `SourceLocation loc` eklensin
|
||||||
|
- [ ] Her düğüm oluşturulurken ilgili token'ın konumu kopyalansın
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Aşama 1: Interpreter (CLI + Dosya)
|
||||||
|
|
||||||
|
> **Hedef**: `saqut` komutu ile REPL veya dosya çalıştırma.
|
||||||
|
|
||||||
|
### 1.1 CLI Altyapısı
|
||||||
|
- [ ] `argc/argv` ile komut satırı argümanları
|
||||||
|
- [ ] `./saqut` → REPL modu (etkileşimli)
|
||||||
|
- [ ] `./saqut file.sqt` → dosyayı çalıştır
|
||||||
|
- [ ] `./saqut --mode=parse file.sqt` → sadece AST üret
|
||||||
|
- [ ] `./saqut --mode=tokens file.sqt` → sadece token listesi
|
||||||
|
- [ ] `./saqut --mode=ir file.sqt` → sadece IR
|
||||||
|
- [ ] `./saqut --mode=symbols file.sqt` → sembol tablosu
|
||||||
|
- [ ] `./saqut --format=json file.sqt` → JSON çıktı
|
||||||
|
|
||||||
|
### 1.2 REPL Modu
|
||||||
|
- [ ] `>` komut istemi
|
||||||
|
- [ ] Her satır ayrı ayrı parse edilir
|
||||||
|
- [ ] `.ast` komutu → son ifadenin AST'sini göster
|
||||||
|
- [ ] `.tokens` komutu → son ifadenin token'larını göster
|
||||||
|
- [ ] `.symbols` komutu → şu ana kadarki sembolleri göster
|
||||||
|
- [ ] `.exit` / `.quit` → çıkış
|
||||||
|
- [ ] Çok satırlı giriş (bloklar için)
|
||||||
|
|
||||||
|
### 1.3 Dosya Modu
|
||||||
|
- [ ] Kaynak dosyayı oku → pipeline'ı çalıştır
|
||||||
|
- [ ] Çıktıyı seçilen formatta bas
|
||||||
|
- [ ] `-o output.json` → dosyaya yaz
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Aşama 2: AST'yi Bellek Canavarı Yapma
|
||||||
|
|
||||||
|
> **Hedef**: AST, kaynak kodun tam bir izdüşümü olsun.
|
||||||
|
> Hiçbir bilgi kaybolmasın. Token'lar AST'de yaşasın.
|
||||||
|
|
||||||
|
### 2.1 Token Tutma
|
||||||
|
- [ ] AST düğümleri token'ları **sahiplensin** (unique_ptr veya shared_ptr)
|
||||||
|
- [ ] `LiteralNode`, `IdentifierNode` → ilgili token'ı doğrudan tutar
|
||||||
|
- [ ] `BinaryExpressionNode` → operatör token'ını tutar
|
||||||
|
- [ ] Her statement → ilgili keyword token'ını tutar (`if` token'ı, `for` token'ı)
|
||||||
|
|
||||||
|
### 2.2 Kaynak Kod Parçası Erişimi
|
||||||
|
- [ ] `ASTNode::getSourceText()` → bu düğümün kaynak koddaki ham metni
|
||||||
|
- [ ] `ASTNode::getSourceRange()` → başlangıç/bitiş SourceLocation
|
||||||
|
- [ ] Hata mesajlarında kaynak kod parçası gösterimi:
|
||||||
|
```
|
||||||
|
Hata: test.sqt:5:10: 'x' değişkeni tanımlı değil
|
||||||
|
4 | int main() {
|
||||||
|
5 | y = x + 1;
|
||||||
|
| ^
|
||||||
|
6 | }
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2.3 AST Görselleştirme
|
||||||
|
- [ ] `--format=json` → tam AST'yi JSON olarak dök
|
||||||
|
- [ ] `--format=dot` → Graphviz DOT formatı (görsel ağaç)
|
||||||
|
- [ ] `--format=html` → Etkileşimli HTML ağaç görünümü (ileride)
|
||||||
|
- [ ] JSON çıktısı her düğüm için:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"kind": "BinaryExpression",
|
||||||
|
"operator": "PLUS",
|
||||||
|
"location": { "file": "test.sqt", "line": 5, "column": 9 },
|
||||||
|
"sourceText": "x + 1",
|
||||||
|
"left": { ... },
|
||||||
|
"right": { ... }
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Aşama 3: Sembol Tablosu
|
||||||
|
|
||||||
|
> **Hedef**: AST kadar zengin, basit yapılı bir sembol tablosu.
|
||||||
|
> Her sembolün tüm kullanım noktaları, tip bilgisi, scope'u kayıtlı.
|
||||||
|
|
||||||
|
### 3.1 Symbol Sınıfı
|
||||||
|
- [ ] `Symbol` yapısı:
|
||||||
|
- `name`: sembol adı
|
||||||
|
- `kind`: variable, function, parameter, type, ...
|
||||||
|
- `type`: tip bilgisi (şimdilik string, ileride Type sistemi)
|
||||||
|
- `definitionLoc`: tanımlandığı SourceLocation
|
||||||
|
- `references`: kullanıldığı tüm SourceLocation'ların listesi
|
||||||
|
- `scope`: ait olduğu scope (global, function, block)
|
||||||
|
- `metadata`: opsiyonel anahtar-değer çiftleri
|
||||||
|
|
||||||
|
### 3.2 SymbolTable Sınıfı
|
||||||
|
- [ ] İç içe scope desteği (global → function → block)
|
||||||
|
- [ ] `enterScope()` / `exitScope()`
|
||||||
|
- [ ] `define(name, kind, type, location)` → yeni sembol ekle
|
||||||
|
- [ ] `resolve(name)` → en yakın scope'tan başlayarak ara
|
||||||
|
- [ ] `addReference(name, location)` → kullanım noktası ekle
|
||||||
|
- [ ] `getAllSymbols()` → tüm scope'lardaki tüm semboller (düz liste)
|
||||||
|
- [ ] JSON'a serialize edilebilme
|
||||||
|
|
||||||
|
### 3.3 Sembol Toplama (AST Walker)
|
||||||
|
- [ ] `SymbolCollector` sınıfı: AST'yi dolaşır, sembolleri toplar
|
||||||
|
- [ ] Değişken tanımlarını yakala → `define()`
|
||||||
|
- [ ] Değişken kullanımlarını yakala → `addReference()`
|
||||||
|
- [ ] Fonksiyon tanımlarını yakala
|
||||||
|
- [ ] Hata durumları: tanımsız değişken, çift tanım, tip uyuşmazlığı
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Aşama 4: Feature Toggle Sistemi
|
||||||
|
|
||||||
|
> **Hedef**: Derleyicinin her özelliği açılıp kapatılabilir olsun.
|
||||||
|
|
||||||
|
### 4.1 CompilerConfig Yapısı
|
||||||
|
- [ ] `CompilerConfig` struct'ı:
|
||||||
|
```cpp
|
||||||
|
struct CompilerConfig {
|
||||||
|
// Dil özellikleri
|
||||||
|
bool enableWhile = true;
|
||||||
|
bool enableFor = true;
|
||||||
|
bool enableDoWhile = true;
|
||||||
|
bool enableSwitch = true;
|
||||||
|
bool enableClass = false; // henüz yok
|
||||||
|
bool enableInterface = false;
|
||||||
|
bool enableEnum = false;
|
||||||
|
|
||||||
|
// Operatörler
|
||||||
|
bool enableTernary = true;
|
||||||
|
bool enablePostfix = true;
|
||||||
|
bool enableUnary = true;
|
||||||
|
|
||||||
|
// Optimizasyon adımları
|
||||||
|
bool optConstantFolding = false;
|
||||||
|
bool optDeadCodeElim = false;
|
||||||
|
bool optUnusedVariable = false;
|
||||||
|
|
||||||
|
// Çıktı
|
||||||
|
std::string outputFormat = "text"; // text, json
|
||||||
|
std::string mode = "run"; // tokens, ast, ir, symbols, run
|
||||||
|
std::string inputFile;
|
||||||
|
std::string outputFile;
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4.2 Keyword Kontrolü
|
||||||
|
- [ ] Tokenizer, config'te kapalı keyword'leri identifier olarak tanısın
|
||||||
|
- [ ] Parser, kapalı keyword'ler için hata değil, görmezden gelsin
|
||||||
|
- [ ] `--disable-while` → `while` artık bir keyword değil
|
||||||
|
|
||||||
|
### 4.3 Optimizasyon Kontrolü
|
||||||
|
- [ ] Her optimizasyon adımı bir `OptimizationPass` soyut sınıfı
|
||||||
|
- [ ] `OptimizationManager`: config'e göre pas'ları çalıştırır
|
||||||
|
- [ ] `--skip-constant-folding` → o adım atlanır
|
||||||
|
- [ ] `--opt-all` → tüm optimizasyonlar aktif
|
||||||
|
- [ ] `--opt-none` → hiçbiri aktif değil
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Aşama 5: Çoklu Backend Altyapısı (IR Merkezli)
|
||||||
|
|
||||||
|
> **Hedef**: Tüm backend'ler aynı IR'den beslenir.
|
||||||
|
> Yeni backend eklemek = yeni bir `IRRunner` yazmak.
|
||||||
|
|
||||||
|
### 5.1 IR'yi Güçlendirme
|
||||||
|
- [ ] Kontrol akışı: `br`, `jmp`, `cmp`, `br_eq`, `br_lt`, `br_gt`
|
||||||
|
- [ ] Fonksiyon: `call`, `ret`, `param`
|
||||||
|
- [ ] Bellek: `load`, `store`, `alloca`
|
||||||
|
- [ ] Tip bilgisi: her IR komutu tipleri taşısın
|
||||||
|
- [ ] Debug bilgisi: kaynak satır eşlemesi
|
||||||
|
- [ ] `IRModule` sınıfı: fonksiyon listesi, global değişkenler, IR komutları
|
||||||
|
|
||||||
|
### 5.2 Backend Arayüzü
|
||||||
|
- [ ] `IBackend` soyut sınıfı:
|
||||||
|
```cpp
|
||||||
|
class IBackend {
|
||||||
|
public:
|
||||||
|
virtual bool initialize(CompilerConfig& config) = 0;
|
||||||
|
virtual bool compile(IRModule& ir) = 0;
|
||||||
|
virtual bool run() = 0;
|
||||||
|
virtual std::string getOutput() = 0;
|
||||||
|
virtual ~IBackend() = default;
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5.3 C Transpile Backend (Aşama 1)
|
||||||
|
- [ ] IR → C kaynak kodu dönüşümü
|
||||||
|
- [ ] Değişken tanımları, if/for/while, fonksiyonlar
|
||||||
|
- [ ] GCC/Clang ile derleme ve çalıştırma
|
||||||
|
- [ ] Üretilen C kodu okunabilir ve debug edilebilir olmalı
|
||||||
|
|
||||||
|
### 5.4 Interpreter Backend (Aşama 1)
|
||||||
|
- [ ] IR'yi doğrudan yürüten sanal makine
|
||||||
|
- [ ] Stack tabanlı veya register tabanlı
|
||||||
|
- [ ] Adım adım çalıştırma (step) desteği
|
||||||
|
- [ ] Değişken değerlerini gösterme
|
||||||
|
|
||||||
|
### 5.5 QBE Backend (Aşama 2)
|
||||||
|
- [ ] QBE C API'si ile entegrasyon
|
||||||
|
- [ ] IR → QBE IR dönüşümü
|
||||||
|
- [ ] Native binary üretimi
|
||||||
|
|
||||||
|
### 5.6 LLVM Backend (Aşama 3, opsiyonel)
|
||||||
|
- [ ] LLVM C API ile entegrasyon
|
||||||
|
- [ ] IR → LLVM IR dönüşümü
|
||||||
|
- [ ] Agresif optimizasyonlar, çok platformlu kod üretimi
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Aşama 6: Hata Sistemi
|
||||||
|
|
||||||
|
> **Hedef**: Sadece hata mesajı değil, **açıklama**.
|
||||||
|
> Kullanıcıya neyi yanlış yaptığını ve nasıl düzelteceğini söyle.
|
||||||
|
|
||||||
|
### 6.1 Diagnostic Sistemi
|
||||||
|
- [ ] `Diagnostic` yapısı: `{level, location, message, hint, sourceLine}`
|
||||||
|
- [ ] Seviyeler: error, warning, note, hint
|
||||||
|
- [ ] Birden fazla diagnostic toplama (ilk hatada durma)
|
||||||
|
- [ ] `DiagnosticEngine`: diagnostic'leri toplar, formatlar, basar
|
||||||
|
|
||||||
|
### 6.2 Hata Mesajı Formatı
|
||||||
|
- [ ] Kaynak satır gösterimi (^^^^ işaretleme)
|
||||||
|
- [ ] Renkli çıktı (opsiyonel, `--color`)
|
||||||
|
- [ ] JSON formatında da alınabilir
|
||||||
|
- [ ] Hata kodları (örn: E0001, W0002)
|
||||||
|
|
||||||
|
### 6.3 Zengin Hata Açıklamaları
|
||||||
|
- [ ] Tanımsız değişken → "x tanımlı değil. Bunu mu demek istediniz: xy?"
|
||||||
|
- [ ] Tip uyuşmazlığı → "int bekleniyor ama float verilmiş. Cast ekleyin."
|
||||||
|
- [ ] Eksik noktalı virgül → "; eksik. Bu satırın sonuna ekleyin."
|
||||||
|
- [ ] Sembol tablosu kullanarak bağlamlı öneriler
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Aşama 7: LSP Sunucusu (Uzun Vade)
|
||||||
|
|
||||||
|
> **Hedef**: VS Code ve diğer IDE'ler için tam dil desteği.
|
||||||
|
> Tüm metadata ve çoklu çıktı formatları LSP'yi kolaylaştırır.
|
||||||
|
|
||||||
|
### 7.1 LSP Protokolü Temelleri
|
||||||
|
- [ ] `initialize`, `shutdown`, `exit`
|
||||||
|
- [ ] `textDocument/didOpen`, `didChange`, `didClose`
|
||||||
|
- [ ] JSON-RPC 2.0 üzerinden iletişim
|
||||||
|
|
||||||
|
### 7.2 Dil Özellikleri
|
||||||
|
- [ ] **Syntax highlighting**: Token listesini LSP'e bildir
|
||||||
|
- [ ] **Diagnostics**: Hata/uyarıları anlık bildir
|
||||||
|
- [ ] **Go to definition**: Sembol tablosundan tanım konumuna git
|
||||||
|
- [ ] **Find references**: Sembolün tüm kullanım noktalarını bul
|
||||||
|
- [ ] **Hover**: İmleç üstündeki sembol hakkında bilgi göster
|
||||||
|
- [ ] **Code completion**: Bağlama göre öneriler (sembol tablosundan)
|
||||||
|
- [ ] **Document symbols**: Dosyadaki tüm sembolleri listele (AST'den)
|
||||||
|
- [ ] **Code lens**: Fonksiyon başına reference sayısı
|
||||||
|
|
||||||
|
### 7.3 Kod Hiyerarşisi Görselleştirme
|
||||||
|
- [ ] `--format=html` ile etkileşimli AST ağacı
|
||||||
|
- [ ] AST'yi JSON olarak alıp web UI'da görselleştirme
|
||||||
|
- [ ] Sembol tablosunu grafik olarak gösterme
|
||||||
|
- [ ] Fonksiyon çağrı grafiği (call graph)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Aşama 8: Test ve Kalite
|
||||||
|
|
||||||
|
### 8.1 Birim Testleri
|
||||||
|
- [ ] Lexer testleri: her sayı formatı, her operatör, string
|
||||||
|
- [ ] Tokenizer testleri: keyword'ler, delimiter'lar, yorumlar
|
||||||
|
- [ ] Parser testleri: her statement tipi, iç içe ifadeler, hata durumları
|
||||||
|
- [ ] IR testleri: her opcode, doğru register tahsisi
|
||||||
|
- [ ] Sembol tablosu testleri: tanımlama, çözümleme, scope
|
||||||
|
|
||||||
|
### 8.2 Anlık Görüntü (Snapshot) Testleri
|
||||||
|
- [ ] `source.sqt` → `output.json` karşılaştırması
|
||||||
|
- [ ] Her commit'te AST/IR/sembol çıktısı değişmemeli (regresyon)
|
||||||
|
|
||||||
|
### 8.3 Benchmark
|
||||||
|
- [ ] Büyük dosya parse süresi
|
||||||
|
- [ ] Tokenizer throughput (token/saniye)
|
||||||
|
- [ ] Bellek kullanımı
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Öncelik Sıralaması
|
||||||
|
|
||||||
|
```
|
||||||
|
Şu an (Aşama 0) ──▶ Metadata ve konum takibi
|
||||||
|
│
|
||||||
|
Haftaya (Aşama 1) ──▶ CLI + REPL + dosya modu
|
||||||
|
│
|
||||||
|
2 hafta (Aşama 2) ──▶ Bellek canavarı AST + JSON çıktı
|
||||||
|
│
|
||||||
|
3 hafta (Aşama 3) ──▶ Sembol tablosu
|
||||||
|
│
|
||||||
|
4 hafta (Aşama 4) ──▶ Feature toggle + CompilerConfig
|
||||||
|
│
|
||||||
|
5 hafta (Aşama 5) ──▶ IR güçlendirme + C transpile backend
|
||||||
|
│
|
||||||
|
6 hafta (Aşama 6) ──▶ Diagnostic + zengin hata mesajları
|
||||||
|
│
|
||||||
|
... (Aşama 7-8) ──▶ LSP, testler, benchmark
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Anlık Görevler (Bu Hafta)
|
||||||
|
|
||||||
|
### Bugün / Yarın
|
||||||
|
- [ ] `SourceLocation` yapısını tanımla (`src/core/location.hpp`)
|
||||||
|
- [ ] `SourceFile` sınıfını yaz (offset → satır/sütun dönüşümü)
|
||||||
|
- [ ] Lexer'a satır/sütun takibi ekle
|
||||||
|
- [ ] `INumber` yerine `SourceLocation` kullanan yeni yapı
|
||||||
|
|
||||||
|
### Bu Hafta
|
||||||
|
- [ ] Token'lara `SourceLocation` ekle
|
||||||
|
- [ ] AST düğümlerine `SourceLocation` ekle
|
||||||
|
- [ ] Token'ları AST'nin sahiplenmesi (unique_ptr)
|
||||||
|
- [ ] CLI modülü: argc/argv, temel komutlar
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Mimari Notlar
|
||||||
|
|
||||||
|
### Neden Bellek Canavarı?
|
||||||
|
Çünkü amaç **bilgiyi korumak**. Derleyiciler genelde parse ettikten sonra
|
||||||
|
kaynak kod bilgisini atar. Biz atmayacağız. Bu sayede:
|
||||||
|
- Hata mesajlarında kaynak kodu gösterebiliriz
|
||||||
|
- IDE'de "go to definition" yapabiliriz
|
||||||
|
- Kodu yeniden oluşturabiliriz (pretty printer)
|
||||||
|
- Dönüşümleri (refactoring) güvenle yapabiliriz
|
||||||
|
|
||||||
|
### Neden Feature Toggle?
|
||||||
|
Çünkü bu bir **toolbox**. Kullanıcı dili özelleştirebilmeli:
|
||||||
|
- Eğitim amaçlı: önce sadece if/else, sonra while ekle
|
||||||
|
- Deney amaçlı: "while olmadan program yazabilir misin?"
|
||||||
|
- Güvenlik: belirli yapıları yasakla (eval, system call)
|
||||||
|
- DSL: kendi alt-dilini oluştur
|
||||||
|
|
||||||
|
### Neden IR Merkezli?
|
||||||
|
IR, tüm backend'lerin ortak dilidir. Yeni bir backend eklemek
|
||||||
|
istediğimizde parser'ı, lexer'ı, optimizer'ı değiştirmemize gerek kalmaz.
|
||||||
|
Sadece IR → Hedef dönüşümü yazarız. Bu, LLVM dışında QBE, Cranelift,
|
||||||
|
hatta kendi JIT'imizi eklemeyi mümkün kılar.
|
||||||
|
|
@ -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ı)
|
||||||
|
|
@ -0,0 +1,33 @@
|
||||||
|
// saQut — geçerli örnek program (semantik analiz + kod üretimi fixture'ı)
|
||||||
|
//
|
||||||
|
// Kilitlenmiş tasarıma uyar: prosedürel, value semantics, kullanıcıya açık
|
||||||
|
// pointer yok, tek main, struct/array gerektirmez. Birinci kilometre taşının
|
||||||
|
// ("fibonacci'yi derle ve çalıştır") referans programıdır.
|
||||||
|
//
|
||||||
|
// İlk ifade doğrudan bir fonksiyon tanımı olabilir; zorunlu class/main
|
||||||
|
// boilerplate'i yoktur (Java'nın aksine).
|
||||||
|
|
||||||
|
int fibonacci(int n) {
|
||||||
|
if (n <= 1) {
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
return fibonacci(n - 1) + fibonacci(n - 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
int fibonacciIterative(int n) {
|
||||||
|
int first = 0;
|
||||||
|
int second = 1;
|
||||||
|
for (int i = 0; i < n; i = i + 1) {
|
||||||
|
int next = first + second;
|
||||||
|
first = second;
|
||||||
|
second = next;
|
||||||
|
}
|
||||||
|
return first;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
int n = 10;
|
||||||
|
print(fibonacci(n)); // recursive
|
||||||
|
print(fibonacciIterative(n)); // iterative
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,5 @@
|
||||||
|
int main() {
|
||||||
|
print("Merhaba");
|
||||||
|
print("saQut calisiyor");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
// Ölü kod eleme örneği
|
||||||
|
// saqut ast file:examples/opt_dce.sqt --optimized
|
||||||
|
// Beklenen: return sonrasındaki deyimler silinir.
|
||||||
|
|
||||||
|
int foo() {
|
||||||
|
return 42;
|
||||||
|
int unreachable = 1;
|
||||||
|
int also_dead = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
int x = foo();
|
||||||
|
return x;
|
||||||
|
print("Bu satır asla çalışmaz");
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
// Sabit katlama örneği
|
||||||
|
// saqut ast file:examples/opt_folding.sqt --optimized
|
||||||
|
// Beklenen: tüm BinaryExpression'lar Literal'e dönüşür.
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
int a = 2 + 3;
|
||||||
|
int b = 10 * 4;
|
||||||
|
int c = 100 / 5;
|
||||||
|
int d = a + 1;
|
||||||
|
return 1 * 10 + 2 * 5;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,89 @@
|
||||||
|
int fibonacci(int n) {
|
||||||
|
if (n <= 1)
|
||||||
|
return n;
|
||||||
|
return fibonacci(n - 1) + fibonacci(n - 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
void fibonacciIterative(int n) {
|
||||||
|
int first = 0, second = 1, next;
|
||||||
|
|
||||||
|
for (int i = 0; i < n; i++) {
|
||||||
|
if (i <= 1)
|
||||||
|
next = i;
|
||||||
|
else {
|
||||||
|
next = first + second;
|
||||||
|
first = second;
|
||||||
|
second = next;
|
||||||
|
}
|
||||||
|
printf("%d ", next);
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
int n = 10;
|
||||||
|
|
||||||
|
// Formatlı ifade kullanmadan, string concatenation mantığı ile
|
||||||
|
printf("");
|
||||||
|
printf(n);
|
||||||
|
printf(" elemanlı Fibonacci dizisi (iterative):\n");
|
||||||
|
fibonacciIterative(n);
|
||||||
|
|
||||||
|
printf("\n");
|
||||||
|
printf("");
|
||||||
|
printf(n);
|
||||||
|
printf(". Fibonacci sayısı (recursive): ");
|
||||||
|
printf(fibonacci(n - 1));
|
||||||
|
printf("\n");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct List {
|
||||||
|
int arr[100];
|
||||||
|
int length;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct List createList() {
|
||||||
|
struct List list;
|
||||||
|
list.length = 0;
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
void push(struct List* list, int value) {
|
||||||
|
if (list->length < 100) {
|
||||||
|
list->arr[list->length] = value;
|
||||||
|
list->length++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int get(struct List* list, int index) {
|
||||||
|
if (index < list->length) {
|
||||||
|
return list->arr[index];
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void printList(struct List* list) {
|
||||||
|
for (int i = 0; i < list->length; i++) {
|
||||||
|
// println yerine direkt yaz
|
||||||
|
int val = list->arr[i];
|
||||||
|
// sayıyı göster
|
||||||
|
}
|
||||||
|
// yeni satır
|
||||||
|
}
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
struct List myList = createList();
|
||||||
|
|
||||||
|
push(myList, 5);
|
||||||
|
push(myList, 10);
|
||||||
|
push(myList, 15);
|
||||||
|
|
||||||
|
// JavaScript benzeri kullanım
|
||||||
|
// myList[0] gibi düşün
|
||||||
|
|
||||||
|
printList(myList);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
# Parser Stres Fixture'ları
|
||||||
|
|
||||||
|
Buradaki dosyalar **yalnızca parser/AST'yi zorlamak** için yazılmıştır.
|
||||||
|
**Geçerli saQut programı değildirler** ve semantik analiz / kod üretimi için
|
||||||
|
fixture olarak **kullanılmamalıdır**.
|
||||||
|
|
||||||
|
## `Final.sqt`
|
||||||
|
|
||||||
|
Tarihsel olarak parser'ın token/AST kapsamını test etmek için kullanıldı
|
||||||
|
(289 token, 200+ AST node). Ancak kilitlenmiş dil tasarımını birden çok yerde
|
||||||
|
**ihlal eder**:
|
||||||
|
|
||||||
|
- **Kullanıcıya açık pointer kullanımı** (`struct List*`, `->`) — dilde `*`/`&`
|
||||||
|
yoktur (bkz. ADR-014).
|
||||||
|
- **Sabit boyutlu array** (`int arr[100]`) — array tipi `int[]`'tir, boyut tipin
|
||||||
|
parçası değildir (bkz. ADR-010).
|
||||||
|
- **İki `int main()` tanımı** — aynı scope'ta çift tanım yasaktır (ADR-011, `E002`).
|
||||||
|
- `printf` format-string semantiği — dilin builtin'i `print`'tir; format'lı
|
||||||
|
çıktı bir kütüphane/FFI işidir (bkz. ADR-017).
|
||||||
|
|
||||||
|
Geçerli, küçük bir örnek için `examples/fibonacci.sqt`'ye bakın.
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
// E008 — yanlış argüman sayısı
|
||||||
|
int add(int a, int b) {
|
||||||
|
return a + b;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
int x = add(1, 2, 3);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
// E003 — return tipi uyuşmazlığı
|
||||||
|
int foo() {
|
||||||
|
return 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
// E004 — break döngü dışında
|
||||||
|
int main() {
|
||||||
|
break;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
// E003 — daraltma: float literal → int
|
||||||
|
int main() {
|
||||||
|
int x = 1.5;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
// W004 — örtük genişletme: değişken→değişken int→float
|
||||||
|
int main() {
|
||||||
|
int a = 5;
|
||||||
|
float b = 1; // OK: literal bağlama-göre tiplenir, uyarısız
|
||||||
|
float c = a; // W004: int değişken → float değişken
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
int main() { int x = 0; while (x < 5) { x = x + 1; } do { x = x - 1; } while (x > 0); return x; }
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
// E010 — döngüsel struct (by-value çevrim → sonsuz boyut)
|
||||||
|
struct A {
|
||||||
|
B b;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct B {
|
||||||
|
A a;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
// E002 — aynı scope'ta çift tanım
|
||||||
|
int main() {
|
||||||
|
int x = 1;
|
||||||
|
int x = 2;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
// E001 — tanımsız isim
|
||||||
|
int main() {
|
||||||
|
int x = y + 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
308
readme.md
308
readme.md
|
|
@ -1,127 +1,225 @@
|
||||||
# Syntax
|
# saQut
|
||||||
|
|
||||||
Syntax modunda 2 seçenek bulunmaktadır
|
**Programlanabilir, incelenebilir bir derleyici — bir "alet çantası" (toolbox).**
|
||||||
- JIR: Söz dizimi tamamen Java programlama diline uygun olarak parse edilmektedir. Compiling, transpiling desteklenir Interpreter desteklenmez
|
|
||||||
- CIR: Söz dizimi tamamen C programlama diline uygun olarak parse edilir. Compiling ve Interpreter desteklenir. transpiling desteklenmez
|
|
||||||
|
|
||||||
# Compiler short options
|
saQut'un asıl varlık sebebi dilin kendisinden çok, **derleme sürecinin her
|
||||||
|
aşamasının dışarıdan görülebilir ve müdahale edilebilir olmasıdır.** Token'lar,
|
||||||
|
AST, sembol tablosu, optimizasyonun öncesi/sonrası ve IR — hepsi ayrı ayrı
|
||||||
|
incelenebilir. Dil, bu aletin üzerinde çalıştığı küçük, prosedürel bir
|
||||||
|
örnektir; vitrin değil, alet.
|
||||||
|
|
||||||
|
Uygulama dili **C++**'tır (header-only eğilimli, bkz. `docs/fikirler.md` ADR-003).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Şu an ne çalışıyor, ne çalışmıyor
|
||||||
|
|
||||||
|
Belgeler **planlanan** ile **yapılan**ı net ayırır. Bugünkü gerçek durum:
|
||||||
|
|
||||||
|
### ✅ Çalışıyor (built)
|
||||||
|
- **Lexer** — karakter seviyesi tarama, konum takibi.
|
||||||
|
- **Tokenizer** — token üretimi (6 token tipi), yorum satırı desteği.
|
||||||
|
- **Pratt parser** — ifade (Pratt) + statement (recursive descent) ayrıştırma.
|
||||||
|
- **AST** — fonksiyon, blok, değişken tanımı, if/for/while/do-while/return,
|
||||||
|
ifade node'ları.
|
||||||
|
- **AST'nin JSON serileştirmesi** — `saqut ast` ile incelenebilir.
|
||||||
|
- **CLI komut yapısı** — `tokens`, `ast`, `symbols`, `run` iskeletleri.
|
||||||
|
- **Kaynak konum takibi** (SourceLocation) — offset → (satır, sütun).
|
||||||
|
- **Minimal IR deneyi** — basit aritmetiği düşürür (örn. `1 + (7/3)` → kısa
|
||||||
|
doğrusal komut dizisi). Henüz tam bir backend değil, bir deneydir.
|
||||||
|
|
||||||
|
### 🚧 Henüz yok (planned)
|
||||||
|
- Sembol tablosu
|
||||||
|
- Semantik analiz
|
||||||
|
- Tip sistemi
|
||||||
|
- Diagnostic (hata raporlama) motoru
|
||||||
|
- Optimizasyon
|
||||||
|
- IR + bytecode VM ile çalıştırma
|
||||||
|
|
||||||
|
> `feature/frontend-analysis` dalı şu an yalnızca bu yapılmamış işin **tasarım
|
||||||
|
> belgelerini** içerir, kodunu değil.
|
||||||
|
|
||||||
|
**Birinci kilometre taşı ("bitti" tanımı):** derleyici **fibonacci'yi**
|
||||||
|
(recursive + iterative) ve basit matematik/döngü programlarını **derleyip
|
||||||
|
çalıştırabilmeli.** Referans program: `examples/fibonacci.sqt`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Dil kimliği (kilitli)
|
||||||
|
|
||||||
|
Prosedürel, **C ailesi sözdizimi**, **value semantics**. İlk ifade doğrudan bir
|
||||||
|
işlem/tanım olabilir; zorunlu class/`main` boilerplate'i yoktur (Java'nın aksine).
|
||||||
|
|
||||||
|
| Özellik | Karar |
|
||||||
|
|---|---|
|
||||||
|
| Class / OOP / kalıtım | **Yok** |
|
||||||
|
| Closure | **Yok** |
|
||||||
|
| Generic | **Yok** |
|
||||||
|
| Kullanıcıya açık pointer (`*` / `&`) | **Yok** — derleyici/runtime içeride pointer'ı serbestçe kullanır |
|
||||||
|
| `struct` | **Var** |
|
||||||
|
| Tipli fonksiyonlar (dönüş + parametre) | **Var** |
|
||||||
|
| Array (`int[]`) | **Var** |
|
||||||
|
| `interface` | **Ertelendi** (v0 değil — gerekçe ADR-018) |
|
||||||
|
| `auto` / tip çıkarımı | **Yok** |
|
||||||
|
| Gizli int↔float dönüşümü | **Yok** (tek istisna: sabit folding) |
|
||||||
|
|
||||||
|
Gerekçe: prosedürel tasarım semantik karmaşıklığı en aza indirir ve hedeflerle
|
||||||
|
(fibonacci, matematik, sıralama, ayrıştırma) örtüşür. Standart C'de `class`
|
||||||
|
yoktur (o C++'tır); C, struct + fonksiyonun yettiğini kanıtlar.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Çalıştırma modeli (kilitli): IR + bytecode VM
|
||||||
|
|
||||||
|
saQut, kendi **IR**'sine derler ve bu IR'yi bir **yorumlayıcı döngü (bytecode
|
||||||
|
VM)** ile çalıştırır.
|
||||||
|
|
||||||
|
- **Tree-walker DEĞİL** (çok yavaş).
|
||||||
|
- **Gerçek makine-kodu JIT DEĞİL.** Makine kodu üretimi (register allocation,
|
||||||
|
ABI/çağırma sözleşmeleri, çalıştırılabilir `mmap` bellek) **kapsam dışıdır** —
|
||||||
|
tek faydası ham hızdır ve hız burada öncelik değildir. Öncelikler
|
||||||
|
**determinizm** ve **incelenebilirliktir**; bytecode VM ikisini de doğrudan
|
||||||
|
sağlar.
|
||||||
|
- **Bellek bu modelde kolaydır:** host (C++) heap'i kullanılır; v0 için özel
|
||||||
|
runtime allocator gerekmez.
|
||||||
|
- **C'ye transpile, geçerli bir İKİNCİ backend olarak ileride kalır** (frontend
|
||||||
|
backend-bağımsızdır — middle-end ayrımının amacı budur, ADR-006). İleride
|
||||||
|
makine kodu istenirse elle code generator yazmak yerine **libgccjit / LLVM'e
|
||||||
|
bağlanılır** — ama bu çok uzak gelecektir.
|
||||||
|
|
||||||
|
> Eski belge/konuşmalarda geçen "JIT" terimi yanlış yönlendiricidir; doğru
|
||||||
|
> çerçeve **IR + VM**'dir.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Tasarım felsefesi — neden saQut farklı?
|
||||||
|
|
||||||
|
saQut "daha iyi bir dil" iddiasında değil. Farkı, **derleyiciyi bir platform**
|
||||||
|
olarak ele almasında. İki taahhüt üstüne kuruludur:
|
||||||
|
|
||||||
|
**1. Cam (gör + sorgula + içine müdahale et).** Her aşama — token, AST, sembol
|
||||||
|
tablosu, tip, IR — kararlı, makine-okur ve **çift yönlü** bir arayüzden
|
||||||
|
erişilebilir olmalıdır. Sadece "dök ve göster" değil: kendi AST'ini verip tip
|
||||||
|
kontrolü isteyebilmeli, IR verip çalıştırabilmelisin. **Turnusol testi:** bir
|
||||||
|
yabancı, yalnızca `saqut ast` + `saqut symbols` çıktısından, bizden habersiz bir
|
||||||
|
LSP yazabiliyor mu? Cevap "evet" ise platform gerçektir.
|
||||||
|
|
||||||
|
**2. Kafes (deterministik + yetenek-güvenli çalıştırma).** Pointer yok, value
|
||||||
|
semantics, scope-tabanlı bellek ve **tek dış-dünya kapısı olan FFI seam**
|
||||||
|
(ADR-016) sayesinde saQut kodu, host'un açıkça izin verdiği fonksiyonlar dışında
|
||||||
|
dünyaya dokunamaz. Bytecode VM deterministiktir (ADR-015): aynı girdi → aynı
|
||||||
|
çıktı → aynı çalışma. "Sadelik" diye tasarlanan bu kararlar aslında bir
|
||||||
|
**yetenek-güvenliği (capability sandbox)** kurar — güvenilmeyen veya
|
||||||
|
AI-üretimi kodu güvenle çalıştırmak için biçilmiş kaftan.
|
||||||
|
|
||||||
|
**Bu ikisinin ödülü — kayıt & tekrar (record-replay).** 🚧 *(vizyon, v0 değil;
|
||||||
|
bkz. issue #94.)* Belirsizliğin tek kaynağı (kullanıcı girdisi, zaman, IO,
|
||||||
|
GC/thread kararları) FFI kapısından geçtiği için, mükemmel tekrar oynatma için
|
||||||
|
**her değişkeni her adımda kaydetmek gerekmez** — yalnızca kapıdan geçen değerler
|
||||||
|
kaydedilir, gerisi VM deterministik olarak yeniden çalıştırılarak üretilir. Boyut
|
||||||
|
gigabayttan kilobayta düşer. Replay modunda FFI çağrıları gerçekten çalışmaz,
|
||||||
|
kaydedilmiş değeri döndürür (dosyayı tekrar silmez, sunucuya tekrar istek atmaz).
|
||||||
|
Böylece "benim makinemde çalışıyor, müşteride patlıyor" sorunu: müşteri bir dump
|
||||||
|
yollar, sen çöküşü adım adım, aynı verilerle geri sararsın.
|
||||||
|
|
||||||
|
> Log, önceden sormayı akıl ettiğin sorulara cevap verir; **tekrar-oynatma,
|
||||||
|
> çöküşten *sonra* aklına gelen sorulara.** Zaman-yolculuğu hata ayıklama ayrı
|
||||||
|
> bir altyapı değildir — cam sorgularına bir **zaman koordinatı** eklemektir.
|
||||||
|
|
||||||
|
⚠️ Bu ödülün bedeli v0'da ödenir: **determinizm kutsaldır ve her belirsizlik
|
||||||
|
kaynağı kayıt-altına-alınabilir tek kapıdan geçmelidir.** Sonradan eklenemez;
|
||||||
|
baştan korunur.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Mimari hatlar
|
||||||
|
|
||||||
```
|
```
|
||||||
sqt kaynak kodunu çallıştırır
|
KAYNAK KOD
|
||||||
saqut file:sourcecode.sqt
|
│ lexer
|
||||||
|
▼
|
||||||
sqt kaynak kodunu C koduna sonrada makine koduna derler. GCC gereklidir
|
TOKEN'LAR ────────────── saqut tokens
|
||||||
saqut compile file:sourcecode.sqt output:program.exe
|
│ parser (Pratt + recursive descent)
|
||||||
|
▼
|
||||||
Derleyicinin olduğu gibi çalıştırılması interpreter moduna alır, konsola yazılan kodları alır çalıştırır outputu loglar
|
AST ──────────────────── saqut ast
|
||||||
saqut
|
│ sembol toplama (iki geçişli) ┐
|
||||||
|
▼ │
|
||||||
Derleyici kodu alır ve IR üretir
|
SEMBOL TABLOSU ───────── saqut symbols │ FRONTEND
|
||||||
saqut parse file:sourcecode.sqt output:program.ces
|
│ semantik analiz (annotation) │ (yapı + anlam)
|
||||||
|
▼ │
|
||||||
Derleyici IRyi alır ve çalıştırır. Burda kaynak kodu veya IR olup olmadığını otomatik anlar
|
ANNOTATE EDİLMİŞ AST ─── saqut ast ┘
|
||||||
saqut file:sourcecode.ces
|
│ optimizasyon (opsiyonel, klon üstünde) ── MIDDLE-END
|
||||||
|
▼
|
||||||
Derleyici IRyi veya kaynak kodunu alır C diline çevirir
|
IR ───────────────────── (planlanan) ┐
|
||||||
saqut transpile file:sourcecode.ces output:program.c
|
│ bytecode VM / yorumlayıcı döngü │ BACKEND
|
||||||
|
▼ │ (çalıştırma + FFI seam)
|
||||||
Derleyici kaynak kodu ASTsini çıkarır ve kaydeder
|
ÇALIŞTIRMA / ÇIKTI ───── saqut run ┘
|
||||||
saqut file:sourcecode.sqt ast:sourcecode.xml
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
- **Frontend** yapıyı ve anlamı modeller (tip, scope, dataflow).
|
||||||
|
- **"Hangi çekirdek, hangi cihaz, ne zaman, hangi çıktı formatı"** runtime/backend
|
||||||
|
meselesidir — frontend'e yüklenmez.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
# Compiler Structure
|
## CLI (mevcut + planlanan)
|
||||||
|
|
||||||
## 1 Source Code
|
```
|
||||||
|
# --- çalışıyor ---
|
||||||
|
saqut tokens file:kaynak.sqt # token listesi
|
||||||
|
saqut ast file:kaynak.sqt # AST (JSON)
|
||||||
|
saqut symbols file:kaynak.sqt # sembol tablosu (iskelet)
|
||||||
|
|
||||||
- Yazılan kaynak kodun derleyiciye aktarılması
|
# --- planlanan ---
|
||||||
- Derleyici için belirlenen seçenekler ile derleyici yapısının yeniden yapılandırılması
|
saqut run file:kaynak.sqt # IR üret + bytecode VM ile çalıştır
|
||||||
- Derleyicinin outputlarının ayarlanması ve çıkışlarının aktarılması
|
saqut ast file:kaynak.sqt --optimized # klon, optimize edilmiş AST (öncesi/sonrası)
|
||||||
|
saqut transpile file:kaynak.sqt -o prog.c # ikinci backend (ileride)
|
||||||
|
```
|
||||||
|
|
||||||
## 2 Lexing
|
Tasarım gereği her aşamanın çıktısı erken bir noktada dosyalanabilir/loglanabilir
|
||||||
|
(programlanabilir derleyici). Token, ham AST, optimize AST ve IR ayrı ayrı
|
||||||
|
kaydedilebilir.
|
||||||
|
|
||||||
Kaynak kodun içindeki tüm harflerin gezilip tek parça büyük bir token listesinin oluşturulması
|
---
|
||||||
|
|
||||||
Bu işlemin sonucunda kaynak kodun içindeki tüm yapılar; semboller, sayılar, stringler ve operatörler olarak 4 kategoriye ayrılır
|
## Batteries / stdlib — kuzey yıldızı, ertelendi
|
||||||
|
|
||||||
## 3 Tokenning
|
Gerçek bir genel sürüm pil ile gelmeli (sıralama, sıkıştırma, kripto,
|
||||||
|
JSON/XML/HTML ayrıştırma). Ama bu **bugünün işi ve v0.1 değildir.**
|
||||||
|
|
||||||
Tüm tokenler gezilerek bir Abstract Syntax Tree ağacı (AST) oluşturulur. Tek düze tokenler bu aşamada hiyerarşik olarak
|
Mimari çerçeve (monolit korkusunu önler): derleyici pilleri çekirdeğine
|
||||||
|
gömmez. Bunun yerine **küçük bir gerçek builtin kümesi** (`print`, temel
|
||||||
|
zorunlular) + **gerisi kütüphane/FFI** ile gelir. "Batteries" sorunu aslında
|
||||||
|
bir **sınır (FFI/link seam) sorunudur**, "zlib'i yeniden yaz" sorunu değil.
|
||||||
|
Sınır bir kez çizilir, piller üstünde sonsuza dek birikir.
|
||||||
|
|
||||||
File -> Class -> Methods -> Expressions / Statements -> Volumes -> Values
|
- **JSON/XML/HTML ayrıştırıcıları saQut'un kendisinde yazılabilir** (string +
|
||||||
|
struct + fonksiyon + kontrol akışı yeter). İlk gerçek demo programları.
|
||||||
|
- **Sıkıştırma/kripto:** denenmiş C kütüphanelerine FFI ile bağlan. **Kripto
|
||||||
|
asla elle yazılmaz.**
|
||||||
|
- **Bugüne tek yansıması:** IR/runtime tasarlanırken **bilinçli bir FFI seam**
|
||||||
|
("host fonksiyonu çağır" deliği) bırakılır. `print` bunu zaten zorlar — bunu
|
||||||
|
kaza değil, **kasıtlı bir mekanizma** yapıyoruz (ADR-016).
|
||||||
|
|
||||||
Şeklinde bir ağaç yapısına kavuşur. Böylece yazılan kaynak kodu anlaşılır ilk yapısına kavuşur
|
---
|
||||||
|
|
||||||
## 4 Parsing
|
## Belge haritası
|
||||||
|
|
||||||
Oluşturulan AST ağacı anlamlaştırılır ve zenginleştirilir. Tanımlanan değerler, fonksiyonlar, classler ve değişkenler belirlenir. Tip kontrolleri ve Syntax hataları burada keşfedilir. Ayrıca Ulaşılamayan kod alanları, sınıfların ulaşılamayan (private) accessorları kontrol edilir, tüm bir kod boyunca class, tipleme, değişken ve döngülerin kullanım adetleri analiz edilir. Sistem içinde kullanılan tüm yapılar için geniş kapsamlı bir Symbol tablosu oluşturulur
|
| Belge | İçerik |
|
||||||
|
|---|---|
|
||||||
|
| `docs/fikirler.md` | ADR-001…005: backend stratejisi, parser, header-only, token, IR |
|
||||||
|
| `docs/adr-frontend-analiz.md` | ADR-006…019: frontend, analiz/optimizasyon, çalıştırma modeli, FFI, interface, bellek |
|
||||||
|
| `docs/roadmap-frontend.md` | Faz-faz uygulama planı (sembol tablosu → fibonacci) |
|
||||||
|
| `docs/transkript-frontend-tasarim.md` | Tasarım oturumunun transkripti |
|
||||||
|
| `examples/fibonacci.sqt` | Geçerli referans program (semantik + kod üretimi fixture'ı) |
|
||||||
|
| `examples/parser-stress/` | Yalnızca parser'ı zorlayan, **geçerli olmayan** fixture'lar |
|
||||||
|
|
||||||
## 5 Optimizing
|
---
|
||||||
|
|
||||||
Zenginleştirilmiş AST üzerindeki analizler üzerinden bazı AST dalları silinir, değiştirilir veya yeni dallar eklenebilir
|
## İlke
|
||||||
|
|
||||||
- Constant Folding : 4 + 1 gibi sonucu belli olan ifadeler 5 olarak tutulur
|
Bir şey çalışmadan önce çerçeve inşa etmekten kaçın. Önce **uçtan uca tek bir
|
||||||
- Dead Code Elimination : returnden sonraki kod bloğunun silinmesi veya if(false) ve dengi statementlerin yapıdan kaldırılması
|
dikey dilim** çalıştır (kaynak → IR → çalıştır; tamsayı aritmetiği + değişken +
|
||||||
- Matematiksel olarak değişmez kodların kaldırılması x * 1, x+0, x * 1 gibi valuelerin direkt x olarak değiştirilmesi
|
kontrol akışı + tek bir `print`). Modülerlik bir kuzey yıldızıdır, v0.1
|
||||||
- Hiç kullanılmayan değişkenlerin kaldırılması
|
gereksinimi değil; ihtiyaç doğmadan eklenen her soyutlama **daha az değil, daha
|
||||||
- Sabit (const) değerlerin döngülerin dışına çıkarılması veya programın globaline taşınması
|
çok** karmaşıklıktır.
|
||||||
- Null Check Elimination : Daha önce nullcheck yapılmış bir değişkenin tekrar nullcheck yapılan kontrollerini devredışı bırakmak
|
|
||||||
- Type Check Elimination : Daha önce typecheck yapılmış bir değişkenin tekrar typecheck yapılan kontrollerini devredışı bırakmak
|
|
||||||
|
|
||||||
## 6 Compiling
|
|
||||||
|
|
||||||
Oluşturulmuş tüm AST ağacını tamamen aynı işi yapan daha alt bir veri kümesine indirgeme işidir. Bellekteki AST yapısı ardışık komutlar dizisine çevrilir (Intermediate Representation) IR daha sonra tekrar okunup çalıştırılabilir.
|
|
||||||
|
|
||||||
Daha sonra IR ile kurulacak yapı ile bazen HeavyIR bazende LightIR üretilir.
|
|
||||||
LightIR, en temiz ve hızlı ancak hiç bir ayrıntı içermeyen koddur. Kaynak kodun çalıştırılması için mükemmel veridir ancak debug verilerinden yoksundur
|
|
||||||
HeavyIR, kaynak kodu verilerinin yanı sıra orjinal AST üzerinde tanımlanmış değişken isimleri ayrıntıları ve tiplemeleride içerir. LightIRye göre daha ayrıntılı ve büyüktür ancak debugging ve kaynak kodun parça parça okunduğu durumlar için (örneğin interpreter) kullanışlıdır
|
|
||||||
|
|
||||||
## 7 Interpreting
|
|
||||||
|
|
||||||
Derleyici HeavyIR üretir ve Symbol tablosunu silmez.
|
|
||||||
Optimizasyonların çoğu kapatılır.
|
|
||||||
Oluşturulmuş HeavyIR kodu çalıştırılır çalışma bittikten sonra stackframe kapatılmaz yeni girişler beklenir. Yeni kaynak kodu girişleri yapıldığında yine derlenir ve anında çalıştırılır. Yeni çalıştırılan kaynak kodu bir önceki stackframe içersinde symbol tablosu dikkate alınarak output üretir böylece önceki değerler halen kullanılabilir
|
|
||||||
|
|
||||||
## or 7 Executing
|
|
||||||
|
|
||||||
Derleyici LightIR üretir IR oluşturulduktan sonra symbol tablosu silinir.
|
|
||||||
Üretilen IR otomatize edilmiş bellekte yüksek performans ile çalıştırılır.
|
|
||||||
|
|
||||||
Derleyici o an ürettiği kaynak kodu anında çalıştırdığı (JIT) gibi
|
|
||||||
Daha önce üretilmiş ve depolanmış IR kodunuda çalıştırabilir
|
|
||||||
|
|
||||||
## or 7 Transpiling
|
|
||||||
|
|
||||||
Derleyici HeavyIR üretir ve Symbol tablosunu silmez.
|
|
||||||
Derleyici IR üretmez bunun yerine AST Üzerinden yeni bir dile çevrilir.
|
|
||||||
Duruma göre daha üst bir seviye dile dönüşüm yapıldığı gibi daha alt bir dile dönüşüm yapılabilir. IR üretilmez bunun yerine alınan kaynak kodu farklı bir kaynak koduna çevrilir
|
|
||||||
|
|
||||||
# programmable compiler
|
|
||||||
|
|
||||||
Derleyici tek seferde kaynak kodu alıp okuyup çalıştırabilir veya derleyebilir.
|
|
||||||
Ayrıca debug ortamlarında veya daha ayrıntılı projelerde derleme anına müdehale edilebilir
|
|
||||||
|
|
||||||
## Stage Session
|
|
||||||
|
|
||||||
Derleyiciye verilen bazı parametreler ile Lexer ve Tokenizer anında bazı işlemlerin yapılması engellenebilir
|
|
||||||
Örneğin accessorler kapatılabilir, hexedecimal sayılar kapatılabilir. Veya class olmadan globale yazılmış kodlar engellenebilir
|
|
||||||
|
|
||||||
## AST Session
|
|
||||||
|
|
||||||
Derleyiciye verilen bazı parametreler ile class yapılarına müdehale edilebilir, tipleme sistemleri sabitlenebilir, tek bir metot içerisine yazılacak kod sınırlanabilir veya for içerisinde for döngüsü kısıtlanabilir. Yazılan kod için analizler sonucunda bazı bloklar görmezden gelinebilir veya manipüle edilebilir
|
|
||||||
|
|
||||||
## Optimizing Session
|
|
||||||
|
|
||||||
Optimizasyon aşamaları parametreler ile tek tek açılabilir veya kapatılabilir
|
|
||||||
|
|
||||||
## Output Session
|
|
||||||
|
|
||||||
Derleyici ürettiği veriyi erken bir aşamada dosyalayabilir ve loglayabilir.
|
|
||||||
Derleyicinin oluşturduğui token, Pure AST, Optimized AST, LightIR veya HeavyIR ayrı ayrı kaydedilebilir
|
|
||||||
|
|
||||||
## Compiling Session
|
|
||||||
|
|
||||||
AST ağacını IRye dönüştürürken neleri aktaracağı derleyici parametre şeklinde verilebilir.
|
|
||||||
IR, yanlızca JIT ve Compiling modunda değiştirilebilir. Interpreter modunda değiştirilmesine izin verilmez
|
|
||||||
IR olarak tipleme, sınıflar, değişken isimleri, tekrarlama kayıtları, işlemler ve statementlerin hangilerinin eklenebileceği değiştirilebilir
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
#!/bin/bash
|
||||||
|
set -e
|
||||||
|
PROJECT_ROOT="$( cd "$( dirname "${BASH_SOURCE[0]}" )/.." &> /dev/null && pwd )"
|
||||||
|
BUILD_DIR="$PROJECT_ROOT/build"
|
||||||
|
mkdir -p "$BUILD_DIR"
|
||||||
|
cd "$BUILD_DIR"
|
||||||
|
cmake -G Ninja -DCMAKE_BUILD_TYPE=Debug ..
|
||||||
|
ninja
|
||||||
|
echo "Derleme başarılı: build/saqut"
|
||||||
|
|
@ -0,0 +1,560 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Faz 0-4 sonrasi icin vizyon/backlog issue'lari.
|
||||||
|
|
||||||
|
Bu issue'lar PLAN degildir; fikir/tartisma kayitlaridir. Faz issue'larindan
|
||||||
|
(#69-73) farkli olarak "Sonuc ve Basari Kriterleri" / "Muhendis olmayan analiz"
|
||||||
|
bolumleri yoktur - bunun yerine "Acik Sorular" bolumu vardir. Iceriklerin
|
||||||
|
zamanla degismesi beklenir.
|
||||||
|
"""
|
||||||
|
from gitea import make_request, REPO_PATH
|
||||||
|
|
||||||
|
L_FIKIR = 66
|
||||||
|
L_IRVM = 67
|
||||||
|
L_MODUL = 68
|
||||||
|
L_TIP = 69
|
||||||
|
L_FFI = 70
|
||||||
|
L_TOOLING = 71
|
||||||
|
L_VIZYON = 72
|
||||||
|
|
||||||
|
issues_data = [
|
||||||
|
# ---------------- IR & Bytecode VM ----------------
|
||||||
|
{
|
||||||
|
"title": "[Fikir] IR Komut Seti Tasarımı (Üç-Adresli Kod / Three-Address Code)",
|
||||||
|
"labels": [L_FIKIR, L_IRVM],
|
||||||
|
"body": """### Giriş (Nedir, Neden Önemli?)
|
||||||
|
Faz 0-4 bitince AST (Abstract Syntax Tree, Soyut Sözdizimi Ağacı) tip ve sembol bilgisiyle dolu olacak. Ama AST hâlâ "ağaç" — VM'in (Bytecode VM, Bayt Kodu Sanal Makinesi) çalıştırabileceği düz bir komut listesi değil. Bu issue, AST'den üretilecek **IR'in (Immediate Representation, Ara Temsil)** komut setini tartışır.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Gelişme (Olası Yaklaşımlar)
|
||||||
|
- **Üç-adresli kod (three-address code):** `t1 = a + b`, `t2 = t1 * c` gibi her komut en fazla bir işlem yapar. Okunabilirlik (alet çantası felsefesiyle uyumlu — `saqut ir` ile basılabilir) ile uygulama kolaylığı arasında iyi bir denge.
|
||||||
|
- Minimal opcode ailesi: aritmetik (`add/sub/mul/div`), karşılaştırma, atama (`store/load`), kontrol akışı (`jump/jump_if_false/label`), fonksiyon (`call/return`), dizi/struct erişimi (`get_field/set_field/get_index/set_index`).
|
||||||
|
- Her IR komutu kaynak `SourceLocation`'ı taşımalı — hata mesajları ve "öncesi/sonrası" karşılaştırması için (alet çantası amacı).
|
||||||
|
- `examples/fibonacci.sqt` için elle IR çıktısı yazılıp hedef format netleştirilebilir (taslak doküman olarak).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Açık Sorular
|
||||||
|
- Üç-adresli kod mu, yoksa stack-tabanlı (yığın tabanlı) bir ara form mu daha basit olur? (VM tasarımıyla bağlantılı, bkz. ilgili issue)
|
||||||
|
- Struct/array gibi değer-semantikli (value semantics) tipler IR seviyesinde nasıl temsil edilir — kopyalama ne zaman gerçek bir IR komutu olur?
|
||||||
|
- Optimizasyon (Faz 4) pass'leri AST üzerinde mi kalır, yoksa IR seviyesine de mi taşınır?
|
||||||
|
|
||||||
|
*İmza/Yorum:* Bu issue, "IR güçlendirme" yol haritasının (roadmap-frontend.md sonu) ilk taslağıdır. Faz 0-4 bitmeden kilitli karar alınmamalı — sadece beyin fırtınası."""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "[Fikir] IR'in Serileştirilmesi ve İncelenebilirliği (`saqut ir` komutu)",
|
||||||
|
"labels": [L_FIKIR, L_IRVM],
|
||||||
|
"body": """### Giriş (Nedir, Neden Önemli?)
|
||||||
|
saQut'un varlık sebebi "her aşamanın dışarıdan görülebilir olması". Token, AST ve sembol tablosu zaten `saqut tokens/ast/symbols` ile JSON olarak görülebiliyor. IR de aynı muameleyi görmeli.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Gelişme (Olası Yaklaşımlar)
|
||||||
|
- `saqut ir file:kaynak.sqt` komutu: AST'den üretilen IR'i hem **insan-okur metin** (örn. assembly benzeri `t1 = a + b`) hem de **JSON** (`--format=json`) olarak basar.
|
||||||
|
- `--optimized` bayrağıyla optimizasyon öncesi/sonrası IR yan yana (diff) gösterilebilir — Faz 4'teki AST öncesi/sonrası karşılaştırmasının IR karşılığı.
|
||||||
|
- Her IR komutuna kaynak satır/sütun referansı eklenip, IR'den kaynağa "geri işaretleme" (source map benzeri) yapılabilir — ileride debugger için temel oluşturur.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Açık Sorular
|
||||||
|
- IR metin formatı kendi mini-sözdizimi mi olacak, yoksa mevcut JSON şemasının bir uzantısı mı?
|
||||||
|
- IR dump'ı dosyaya yazılıp tekrar VM'e beslenebilir mi (round-trip) — bu, derleme adımlarını birbirinden bağımsız test etmeyi kolaylaştırır.
|
||||||
|
|
||||||
|
*İmza/Yorum:* Bu, "programlanabilir derleyici" vaadinin IR ayağı. Küçük ama saQut'un kimliği için sembolik önemde."""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "[Fikir] Bytecode Formatı: Yığın-Tabanlı mı (Stack-based), Register-Tabanlı mı VM?",
|
||||||
|
"labels": [L_FIKIR, L_IRVM],
|
||||||
|
"body": """### Giriş (Nedir, Neden Önemli?)
|
||||||
|
ADR-015 çalıştırma modelini kilitledi: IR + bytecode VM (yorumlayıcı döngü), gerçek makine-kodu JIT yok. Ama VM'in *iç mimarisi* henüz seçilmedi: **yığın-tabanlı (stack-based, Python/JVM/Wasm tarzı)** mi, **register-tabanlı (Lua 5'in VM'i tarzı, sanal register'lar)** mı?
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Gelişme (Olası Yaklaşımlar)
|
||||||
|
- **Yığın-tabanlı:** Komutlar değer push/pop eder (`push a`, `push b`, `add`). Üretmesi basit, IR'den bytecode'a çeviri kolay. Dezavantaj: gereksiz push/pop'lar (performans, ama saQut için öncelik değil — bkz. ADR-015 "determinizm/incelenebilirlik > hız").
|
||||||
|
- **Register-tabanlı:** Her fonksiyonun sanal register'ları olur (`add r1, r2, r3`). Daha az komut, ama derleyici tarafı (register allocation - kayıt tahsisi) daha karmaşık.
|
||||||
|
- saQut'un önceliği **basitlik + incelenebilirlik** olduğu için yığın-tabanlı VM ile başlamak "önce dikey dilim" ilkesine daha uygun olabilir; register-tabanlı'ya geçiş ileride bir "VM v2" issue'su olabilir.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Açık Sorular
|
||||||
|
- Yığın-tabanlı seçilirse, fonksiyon çağrıları için ayrı bir "call stack" mı, tek bir birleşik yığın mı kullanılır?
|
||||||
|
- Debug/inceleme modunda her adımdan sonra yığının tam içeriği dump edilebilir mi (alet çantası vaadiyle doğrudan ilişkili)?
|
||||||
|
|
||||||
|
*İmza/Yorum:* Bu karar IR komut setini (önceki issue) doğrudan etkiler — birlikte tartışılmalı. Önerim: v0 için yığın-tabanlı, basitliği ve incelenebilirliği önceliklendirdiği için."""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "[Fikir] VM Yorumlayıcı Döngüsü ve Dispatch Stratejisi",
|
||||||
|
"labels": [L_FIKIR, L_IRVM],
|
||||||
|
"body": """### Giriş (Nedir, Neden Önemli?)
|
||||||
|
Bytecode VM'in kalbi, her komutu okuyup ilgili işlemi yapan "yorumlayıcı döngü" (interpreter loop / dispatch loop). Bu döngünün nasıl yazılacağı hem performansı hem de kod okunabilirliğini etkiler.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Gelişme (Olası Yaklaşımlar)
|
||||||
|
- **Switch-case dispatch:** `switch(opcode) { case ADD: ...; case JUMP: ...; }`. En basit, en okunabilir, derleyici bağımsız (portable). saQut'un "incelenebilir" felsefesiyle en uyumlu başlangıç noktası.
|
||||||
|
- **Computed goto / jump table:** Daha hızlı ama derleyiciye özel uzantılar gerektirir (GCC/Clang `&&label`), taşınabilirlik düşer. Hız öncelik değilse (ADR-015) v0'da gereksiz karmaşıklık.
|
||||||
|
- **Threaded code:** Her IR komutunu doğrudan bir C++ fonksiyon pointer'ına çeviren ileri teknik — şimdilik kapsam dışı.
|
||||||
|
- v0 önerisi: basit switch-case + her adımda "trace" modu (her komut çalışmadan önce/sonra durum dump edilebilir, ADR-016 FFI seam ile `print` zaten bu yola hazırlanıyor).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Açık Sorular
|
||||||
|
- Yorumlayıcı tek bir `run()` fonksiyonu mu olacak, yoksa her opcode için ayrı `handle_*` fonksiyonu mu (okunabilirlik vs. fonksiyon çağrı maliyeti — ama maliyet öncelik değil)?
|
||||||
|
- Adım-adım çalıştırma (step) ve "tüm programı çalıştır" (run) aynı döngüyü mü paylaşacak?
|
||||||
|
|
||||||
|
*İmza/Yorum:* "Önce dikey dilim" ilkesi burada da geçerli: en basit switch-case ile fibonacci çalışsın, optimize dispatch ileri bir konu."""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "[Fikir] Fonksiyon Çağrı Mekanizması ve Call Frame Tasarımı",
|
||||||
|
"labels": [L_FIKIR, L_IRVM],
|
||||||
|
"body": """### Giriş (Nedir, Neden Önemli?)
|
||||||
|
`examples/fibonacci.sqt` özyinelemeli (recursive) bir fonksiyon. VM'in fonksiyon çağrılarını (call/return), parametreleri ve yerel değişkenleri nasıl tuttuğu — yani **call frame (çağrı çerçevesi)** tasarımı — özyinelemenin doğru çalışması için kritik.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Gelişme (Olası Yaklaşımlar)
|
||||||
|
- Her fonksiyon çağrısında bir "frame" oluşturulur: dönüş adresi, parametreler, yerel değişkenler için ayrılan yer. Bu frame'ler bir "call stack" üzerinde tutulur (host C++ heap'i üzerinde — ADR-015, özel allocator yok).
|
||||||
|
- Özyinelemeli çağrılar (fibonacci(n-1), fibonacci(n-2)) her biri kendi frame'ine sahip olmalı — değişkenler birbirine karışmamalı (scope-based memory model, ADR-014).
|
||||||
|
- Maksimum çağrı derinliği (stack overflow koruması) — basit bir sayaç/limit ile "stack taşması" hatası verilebilir (yeni bir E-kodu olabilir, ileride katalogla birleştirilir).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Açık Sorular
|
||||||
|
- Frame boyutu derleme zamanında mı (compile-time, fonksiyonun yerel değişken sayısına göre) hesaplanır, yoksa dinamik mi büyür?
|
||||||
|
- Fonksiyon dönüş değeri yığının üstüne mi konur (yığın-tabanlı VM ile uyumlu), yoksa ayrı bir "return register" mı kullanılır?
|
||||||
|
|
||||||
|
*İmza/Yorum:* Bu issue VM tasarımı (önceki issue) seçimine bağımlıdır; ama fibonacci'nin "bitti" tanımı (recursive + iterative) bu mekanizma olmadan anlamsızdır — birinci öncelikli IR/VM konusu."""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "[Fikir] Array ve Struct'ların Runtime Bellek Düzeni",
|
||||||
|
"labels": [L_FIKIR, L_IRVM],
|
||||||
|
"body": """### Giriş (Nedir, Neden Önemli?)
|
||||||
|
Dil kimliği `struct` ve `int[]` (array/dizi) içeriyor, value semantics (değer semantiği) ile. VM çalışırken bu değerlerin bellekte nasıl yer alacağı — kopyalama ne zaman olur, struct içinde struct/array nasıl saklanır — netleşmeli.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Gelişme (Olası Yaklaşımlar)
|
||||||
|
- Value semantics demek: `struct Point p2 = p1;` ifadesi `p1`'in **tam kopyasını** oluşturur (referans değil). VM'de bu, struct'ın tüm alanlarının yığın/heap'te kopyalanması anlamına gelir.
|
||||||
|
- Array boyutu derleme zamanında sabit mi (E009 hatası bunu zaten kontrol ediyor), yoksa dinamik büyüyebilen bir array tipi de mi olacak (`std::vector` benzeri, host heap üzerinde)?
|
||||||
|
- Struct içinde struct: iç içe value-copy maliyeti büyüyebilir — ama saQut'ta "hız öncelik değil" (ADR-015), önce doğruluk.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Açık Sorular
|
||||||
|
- Dinamik boyutlu array (`int[]` boyutu çalışma zamanında belirlenen) v0 kapsamında mı, yoksa v1'e mi bırakılır?
|
||||||
|
- Struct kopyalama IR'de tek bir "copy" komutu mu olur, yoksa alan-alan kopyalama komut dizisine mi açılır (incelenebilirlik açısından ikincisi daha "alet çantası" uyumlu olabilir)?
|
||||||
|
|
||||||
|
*İmza/Yorum:* ADR-014'teki "scope-based bellek artık gerekçeli" notuyla doğrudan ilişkili — bu issue o kararın runtime'a yansımasıdır."""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "[Fikir] IR Seviyesinde Kontrol Akışı Grafiği (CFG) ve SSA — Gerekli mi?",
|
||||||
|
"labels": [L_FIKIR, L_IRVM],
|
||||||
|
"body": """### Giriş (Nedir, Neden Önemli?)
|
||||||
|
Çoğu "gerçek" derleyici (GCC, LLVM) IR'i bir **CFG (Control Flow Graph, Kontrol Akışı Grafiği)** üzerinde **SSA (Static Single Assignment, Statik Tekil Atama)** formunda tutar. Bu, ileri optimizasyonları (constant propagation, dead code elimination'ın güçlü hali) çok kolaylaştırır ama ciddi bir mühendislik yatırımıdır.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Gelişme (Olası Yaklaşımlar)
|
||||||
|
- saQut'un Faz 4 optimizasyonları (constant folding, dead code elimination) şu an **AST üzerinde** çalışıyor ve bu yeterli olabilir — CFG/SSA şart değil.
|
||||||
|
- CFG/SSA'nın getirisi: döngü içindeki optimizasyonlar, daha güçlü dead-code analizi. Maliyeti: yeni bir ara temsil katmanı, "basit kalsın" ilkesiyle gerilim yaratabilir.
|
||||||
|
- Orta yol: basit IR (üç-adresli kod, lineer komut listesi + `jump`/`label`) yeterli olduğu sürece CFG/SSA'ya **geçilmez**; ancak optimizasyon ihtiyaçları büyürse (örn. döngü-invariant kod taşıma) bu issue tekrar açılır.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Açık Sorular
|
||||||
|
- "Yeterlilik" sınırı nasıl ölçülür? Belki: fibonacci + birkaç döngü/dizi örneği optimize edildiğinde gözle görülür iyileşme varsa CFG/SSA'ya gerek yoktur.
|
||||||
|
- CFG olmadan da basit "basic block" (temel blok) kavramı IR'e eklenebilir mi (sadece jump hedeflerini gruplamak için, SSA olmadan)?
|
||||||
|
|
||||||
|
*İmza/Yorum:* Bu issue kasıtlı olarak "yapma" eğiliminde — erken soyutlama riskine karşı bir hatırlatma (roadmap'teki "önce dikey dilim" ilkesi)."""
|
||||||
|
},
|
||||||
|
# ---------------- Moduller / Import ----------------
|
||||||
|
{
|
||||||
|
"title": "[Fikir] Import Sözdizimi ve Modül Çözümleme",
|
||||||
|
"labels": [L_FIKIR, L_MODUL],
|
||||||
|
"body": """### Giriş (Nedir, Neden Önemli?)
|
||||||
|
Şu ana kadar tüm tasarım **tek dosyalı** programları (`examples/fibonacci.sqt`) hedefliyor. Ama gerçek projeler birden fazla dosyaya bölünür. saQut'a "başka bir dosyadaki fonksiyon/struct'ı kullan" diyebilmek için bir **import (içe aktarma)** sözdizimi gerekir.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Gelişme (Olası Yaklaşımlar)
|
||||||
|
- En basit model: `import "util.sqt"` — dosya yolu tabanlı, C'nin `#include`'una benzer ama metin yapıştırma değil, **sembol tablosu seviyesinde birleştirme** (her dosya kendi AST'sine sahip kalır, sembol çözümleme dosyalar arası genişler).
|
||||||
|
- Alternatif: isim-tabanlı modül sistemi (`import util` → `util.sqt` veya `util/` dizini arar), Go/Python tarzı.
|
||||||
|
- saQut'ta `class`/namespace yok — modül adı, dosya adına eşlenebilecek doğal bir "isim alanı" (namespace) adayı olabilir: `util.helper()` gibi nokta-erişimi.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Açık Sorular
|
||||||
|
- Döngüsel import (A, B'yi; B, A'yı import ediyor) nasıl ele alınır — derleme hatası mı, yoksa forward-declaration ile mi çözülür (ADR-011'deki forward-reference mantığına benzer)?
|
||||||
|
- Import edilen dosyanın AST'si her derlemede yeniden mi ayrıştırılır, yoksa bir "derleme birimi cache"i mi olur (LSP issue'suyla bağlantılı)?
|
||||||
|
|
||||||
|
*İmza/Yorum:* Bu, "Faz 5: Modüller" için doğal bir başlangıç noktası olabilir — ama Faz 0-4 (tek dosya, fibonacci) bitmeden ele alınmamalı."""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "[Fikir] Modüller Arası Görünürlük (public/private) ve İsim Çakışmaları",
|
||||||
|
"labels": [L_FIKIR, L_MODUL],
|
||||||
|
"body": """### Giriş (Nedir, Neden Önemli?)
|
||||||
|
İmport sistemi geldiğinde, "her şey her yerden görülebilir mi" sorusu ortaya çıkar. Bu, sembol tablosunun (Faz 2) modüller arası nasıl genişletileceğiyle doğrudan ilgili.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Gelişme (Olası Yaklaşımlar)
|
||||||
|
- En basit kural: bir dosyadaki **tüm üst-seviye tanımlar** (fonksiyon, struct, global değişken) import eden dosyaya açık olur — "herkese açık" (public-by-default), C'nin extern'siz fonksiyonlarına benzer. Basitlik açısından v0 için cazip.
|
||||||
|
- Daha kontrollü model: `private` anahtar kelimesi ile bir dosyaya özel tanımlar işaretlenebilir — ama bu, dil kimliğine yeni bir anahtar kelime ekler (ADR'lerle uyumlu mu tartışılmalı).
|
||||||
|
- İsim çakışması (iki modülde aynı isimde fonksiyon): modül-öneki (`util.process()`) zorunlu kılınarak çözülebilir — bu, import sözdizimi issue'suyla birlikte kararlaştırılmalı.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Açık Sorular
|
||||||
|
- "Herkese açık" model, sembol tablosunun global scope'unu modüller arasında nasıl birleştirir — her modül kendi global scope'una mı sahip olur, yoksa tek bir "program-geneli" scope mu?
|
||||||
|
- Bu karar E002 (çift tanım) hatasının kapsamını değiştirir mi — aynı isim iki modülde tanımlanırsa hata mı, yoksa öneklemeyle ayrışır mı?
|
||||||
|
|
||||||
|
*İmza/Yorum:* Önerim: v0 için "herkese açık, modül-önekiyle eriş" — basit ve declare-before-use (ADR-011) felsefesiyle tutarlı."""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "[Fikir] Çoklu Dosya Derleme ve Bağımlılık Sırası",
|
||||||
|
"labels": [L_FIKIR, L_MODUL],
|
||||||
|
"body": """### Giriş (Nedir, Neden Önemli?)
|
||||||
|
Import sistemi geldiğinde, `saqut run main.sqt` komutu artık tek dosya değil, bir **bağımlılık ağacı** (dependency graph) derlemek zorunda kalır. Bu issue, CLI ve derleme akışının (pipeline) buna nasıl uyacağını tartışır.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Gelişme (Olası Yaklaşımlar)
|
||||||
|
- Derleyici önce tüm `import` ifadelerini tarayıp bir **dosya bağımlılık grafiği** çıkarır (basit DFS/topological sort — ADR-011'deki struct çevrim kontrolüyle aynı algoritmik aile).
|
||||||
|
- Her dosya kendi AST'sine ve (Faz 2 sonrası) kendi sembol tablosu parçasına sahip olur; sembol çözümleme son adımda birleştirilir.
|
||||||
|
- "Programlanabilir derleyici" felsefesiyle: `saqut ast --module=util.sqt` gibi tek bir modülün AST'si de ayrı incelenebilir kalmalı — çoklu dosya derleme tek-dosya inceleme yeteneğini kırmamalı.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Açık Sorular
|
||||||
|
- Döngüsel bağımlılık (önceki issue'da bahsedilen) burada nasıl raporlanır — yeni bir E-kodu mu (örn. `E011`)?
|
||||||
|
- Derleme çıktısı (IR/bytecode) tek bir birleşik dosya mı olur, yoksa modül-başına ayrı IR mi üretilip VM'de "link" edilir (gerçek linker'lara benzer ama çok daha basit)?
|
||||||
|
|
||||||
|
*İmza/Yorum:* Bu konunun karmaşıklığı yüksek olabilir — "önce dikey dilim" ilkesi burada özellikle geçerli: tek-dosya fibonacci bitmeden bu issue'ya başlanmamalı."""
|
||||||
|
},
|
||||||
|
# ---------------- Tip Sistemi Genişletmeleri ----------------
|
||||||
|
{
|
||||||
|
"title": "[Fikir] Native Decimal Tipi (Ondalıklı Sayılarda Hassasiyet)",
|
||||||
|
"labels": [L_FIKIR, L_TIP],
|
||||||
|
"body": """### Giriş (Nedir, Neden Önemli?)
|
||||||
|
`float`/`double` ikili (binary) kayan-nokta sayılardır ve ondalık kesirleri (örn. `0.1`) tam temsil edemez — bu, para/finans hesaplamalarında hatalara yol açar. Birçok modern dil (C#, Python `Decimal`) bunun için ayrı bir **decimal (ondalık) tip** sunar.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Gelişme (Olası Yaklaşımlar)
|
||||||
|
- `decimal` yeni bir primitif `TypeKind` olarak `src/core/type.hpp`'ye eklenebilir (Faz 0'ın `Type` sınıfı genişletilebilir tasarlanmalı — bu issue o genişletilebilirliği şimdiden akılda tutmak için var).
|
||||||
|
- Runtime temsili: sabit-noktalı (fixed-point) tamsayı + ölçek (scale) çifti (örn. "scaled integer" — `12345` ve ölçek `2` → `123.45`). Host heap'te basit bir struct olarak tutulabilir, özel kütüphane gerekmez (v0).
|
||||||
|
- Literal Adaptasyon Kuralı (ADR-010) ile ilişki: `decimal x = 1;` geçerli olmalı (tamsayı → decimal kayıpsız); `decimal x = 1.5;` (float literal → decimal) hassasiyet tartışması gerektirir.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Açık Sorular
|
||||||
|
- `decimal` aritmetiği (`+`, `*`) için taşma (overflow) kuralları ne olacak — hata mı, yoksa otomatik büyüme mi (bu, "host heap, özel allocator yok" ADR-015 ilkesiyle gerilim yaratabilir)?
|
||||||
|
- Bu tip v0.1'in parçası mı, yoksa "batteries" (ADR-017) kapsamında ileride bir FFI/kütüphane çözümü mü?
|
||||||
|
|
||||||
|
*İmza/Yorum:* "Modern dillerin standart hale getirdiği ama derleyici çekirdeğine gömülmesi gereken" tiplerin ilk örneği — kullanıcı isteğiyle doğrudan örtüşüyor."""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "[Fikir] Native Date/Time (Tarih/Zaman) Tipi",
|
||||||
|
"labels": [L_FIKIR, L_TIP],
|
||||||
|
"body": """### Giriş (Nedir, Neden Önemli?)
|
||||||
|
Tarih/zaman, hemen her gerçek programda karşılaşılan ama yanlış yapılması çok kolay bir konudur (saat dilimleri, artık yıllar, formatlar). Modern diller (Go, Rust, JS `Temporal`) bunu standart kütüphaneye/derleyiciye yakın tutar.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Gelişme (Olası Yaklaşımlar)
|
||||||
|
- v0 için minimal yaklaşım: `date`/`datetime` yeni bir primitif tip olarak değil, **struct olarak saQut'un kendisinde** tanımlanabilir (`struct Date { int year; int month; int day; }`) — bu, "batteries = FFI/kütüphane sınırı" (ADR-017) felsefesiyle örtüşür ve derleyici çekirdeğine yük bindirmez.
|
||||||
|
- Daha ileri: gerçek saat/takvim hesapları (örn. "bugünden 30 gün sonrası") host sisteminden FFI (ADR-016, `callhost`) ile alınabilir — saat dilimi/takvim mantığı saQut içine yazılmaz, denenmiş bir C kütüphanesine (örn. sistem `time.h`) bağlanır.
|
||||||
|
- Eğer ileride performans/ergonomi nedeniyle native bir `date` tipi gerekirse, bu issue "native tip mi, struct+FFI mi" kararının kaydı olarak kalır.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Açık Sorular
|
||||||
|
- "Şu anki zaman" gibi dış-dünya bağımlı bilgi nasıl elde edilir — bu doğrudan FFI seam'in (`print` sonrası) ikinci gerçek kullanım örneği olabilir mi?
|
||||||
|
- Tarih aritmetiği (`tarih + 5 gün`) operatör overload'ı gerektirir mi — ama dilde operatör overload yok (kilitli karar); bu çatışmayı nasıl çözeriz (fonksiyon: `addDays(d, 5)`)?
|
||||||
|
|
||||||
|
*İmza/Yorum:* Önerim: v0'da struct+FFI, native tip değil — "batteries boundary" ilkesinin ikinci somut test vakası."""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "[Fikir] Enum (Sabit Kümesi) Desteği",
|
||||||
|
"labels": [L_FIKIR, L_TIP],
|
||||||
|
"body": """### Giriş (Nedir, Neden Önemli?)
|
||||||
|
Şu anki dil kimliğinde `enum` yok. Ama "renk", "durum", "yön" gibi sabit bir küme içinden değer alan değişkenler çok yaygındır ve bunları `int` ile temsil etmek (örn. `0=KIRMIZI, 1=MAVI`) hem hata-eğilimli hem de okunaksızdır.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Gelişme (Olası Yaklaşımlar)
|
||||||
|
- En basit model: `enum Renk { KIRMIZI, MAVI, YESIL }` — derleme zamanında `int`'e eşlenen adlandırılmış sabitler. Tip denetimi (Faz 3) açısından `Renk` ayrı bir `TypeKind` (ya da struct'a benzer adlandırılmış-tip) olarak ele alınabilir; `int`'le gizli dönüşüm yasak (ADR-010 ile tutarlı — `Renk x = 0;` yerine `Renk x = KIRMIZI;`).
|
||||||
|
- `switch`/`match` ile birlikte düşünülürse (dilde şu an `switch` yok, sadece `if`) enum'un asıl gücü ortaya çıkar — bu issue, ileride bir `switch` tartışmasını da tetikleyebilir.
|
||||||
|
- Struct'lara ek yük getirmez, sembol tablosuna (Faz 2) yeni bir `SymbolKind::EnumValue` eklenmesi yeterli olabilir.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Açık Sorular
|
||||||
|
- Enum değerleri sadece `int` mi temsil eder, yoksa ileride `string` etiketli enum (örn. JSON serileştirmede yararlı) da düşünülür mü?
|
||||||
|
- `interface` gibi enum da "ertelendi" listesine mi girer, yoksa v0.1'e (fibonacci sonrası ilk gerçek dil-genişletmesi) yakın bir öncelik mi?
|
||||||
|
|
||||||
|
*İmza/Yorum:* Düşük karmaşıklıkta, yüksek günlük-kullanım faydası olan bir özellik — "fibonacci sonrası ilk küçük kazanımlar" listesine iyi bir aday."""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "[Fikir] String'in İç Temsili ve Unicode/UTF-8 Stratejisi",
|
||||||
|
"labels": [L_FIKIR, L_TIP],
|
||||||
|
"body": """### Giriş (Nedir, Neden Önemli?)
|
||||||
|
`string` tipi dil kimliğinde var ama "string nedir" sorusu (bayt dizisi mi, karakter dizisi mi, UTF-8 mi) henüz hiç tartışılmadı. Bu karar, `length()`, indeksleme (`s[0]`), ve dosya/konsol I/O'sunun davranışını doğrudan belirler.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Gelişme (Olası Yaklaşımlar)
|
||||||
|
- **UTF-8 bayt dizisi (Go/Rust tarzı):** En basit runtime temsili (host C++ `std::string`'e doğrudan eşlenir, ADR-015 "host heap" ilkesiyle uyumlu). Ama `s[0]` bir bayt döner, bir "karakter" değil — Türkçe karakterler (ç, ş, ğ) çok-baytlı olduğundan indeksleme kafa karıştırabilir.
|
||||||
|
- **Kod-noktası (code point) dizisi:** Her "karakter" tek birim — daha sezgisel ama dönüşüm/depolama maliyeti var.
|
||||||
|
- v0 önerisi: UTF-8 bayt temsilini benimse (C++'la doğal uyum), ama `length()`/indeksleme dokümantasyonunda "bayt sayısı/bayt indeksi" olduğu açıkça yazılsın — ileride `runeAt()`/`codepointLength()` gibi yardımcı builtin'lerle (ADR-016/017) kod-noktası erişimi sağlanabilir.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Açık Sorular
|
||||||
|
- String **immutable (değişmez)** mi olacak (Java/Python tarzı, value-semantics ile de uyumlu) yoksa mutable mi?
|
||||||
|
- String birleştirme (`+`) ve karşılaştırma operatörleri tip denetiminde (Faz 3) nasıl ele alınacak — `string + int` gizli dönüşüm mü (ADR-010 "gizli dönüşüm yok" ile çatışır, muhtemelen `E003`)?
|
||||||
|
|
||||||
|
*İmza/Yorum:* Bu karar ne kadar erken alınırsa, sonraki tüm builtin/stdlib tasarımı (özellikle JSON/XML parser örnekleri, ADR-017) o kadar sağlam temellenir."""
|
||||||
|
},
|
||||||
|
# ---------------- FFI / Builtin / Stdlib ----------------
|
||||||
|
{
|
||||||
|
"title": "[Fikir] FFI Seam Detaylı Tasarımı — `callhost` İmzası ve Tip Eşleme (Marshaling)",
|
||||||
|
"labels": [L_FIKIR, L_FFI],
|
||||||
|
"body": """### Giriş (Nedir, Neden Önemli?)
|
||||||
|
ADR-016, "host fonksiyonu çağır" (FFI — Foreign Function Interface, Yabancı Fonksiyon Arayüzü) mekanizmasının **kasıtlı** bir tasarım kararı olduğunu, `print`'in ilk müşterisi olacağını söylüyor. Ama mekanizmanın somut hali (fonksiyon imzası, parametre/dönüş tipi eşlemesi) henüz tasarlanmadı.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Gelişme (Olası Yaklaşımlar)
|
||||||
|
- VM'de bir `callhost <isim> <argüman sayısı>` IR/bytecode komutu: argümanlar yığından alınır, C++ tarafındaki kayıtlı bir fonksiyon tablosuna (`std::map<std::string, HostFunction>`) bakılır, sonuç yığına geri konur.
|
||||||
|
- Tip eşleme: saQut `int`/`float`/`string`/`bool` değerleri C++ `int`/`double`/`std::string`/`bool`'a nasıl çevrilir (marshaling) — basit primitiflerle başlamak ("önce dikey dilim"), struct/array marshaling'i ileri bir adım olarak bırakmak.
|
||||||
|
- Hata yönetimi: host fonksiyon başarısız olursa (örn. dosya bulunamadı) bu, saQut tarafına nasıl yansır — bir hata kodu mu, yoksa `panic` benzeri bir çalışma-zamanı hatası mı (yeni runtime hata kategorisi gerekebilir)?
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Açık Sorular
|
||||||
|
- Host fonksiyon tablosu derleme zamanında mı sabit (yalnızca derleyicinin sunduğu `print` gibi fonksiyonlar), yoksa kullanıcı kendi C++ fonksiyonunu kayıt edebilir mi (gömülü/embeddable derleyici senaryosu — "alet çantası" ruhuna çok uygun ama kapsam büyütür)?
|
||||||
|
- `callhost` IR seviyesinde mi, yoksa daha yüksek seviyeli bir AST-annotation mı (Faz 1'in `ExpressionNode` alanlarına eklenebilir)?
|
||||||
|
|
||||||
|
*İmza/Yorum:* `print(fibonacci(n))` çalıştığı gün, bu issue'nun "v0" kapsamı fiilen tamamlanmış olacak — minimal hedef gayet net."""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "[Fikir] Builtin Fonksiyon Kataloğu ve Çözümleme Mekanizması",
|
||||||
|
"labels": [L_FIKIR, L_FFI],
|
||||||
|
"body": """### Giriş (Nedir, Neden Önemli?)
|
||||||
|
`print` ilk builtin (yerleşik) fonksiyon. Ama "builtin" kelimesinin derleyici içinde nasıl bir yeri olacağı — sembol tablosunda mı, ayrı bir kayıt defterinde mi — henüz netleşmedi. Bu issue, `print`'ten sonra gelecek `len`, `toString`, `parseInt` gibi fonksiyonların **sistematik** bir şekilde eklenmesini planlar.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Gelişme (Olası Yaklaşımlar)
|
||||||
|
- Faz 2'nin Symbol Table'ı (sembol tablosu) başlangıçta "önceden tanımlı" (pre-defined) bir global scope ile kurulabilir — her builtin, kullanıcı kodu hiç okunmadan önce bu scope'a `SymbolKind::Function` olarak eklenir. Böylece `print(x)` çağrısı normal bir fonksiyon çağrısı gibi tip denetiminden (Faz 3) geçer.
|
||||||
|
- Builtin kataloğu merkezi bir dosyada (`src/runtime/builtins.hpp` gibi) tek listede tutulur: isim, parametre tipleri, dönüş tipi, ve VM'deki karşılığı (FFI üzerinden mi, yoksa doğrudan bir IR opcode'u mu).
|
||||||
|
- İlk aday liste (fibonacci sonrası "küçük kazanımlar"): `print`, `len` (string/array uzunluğu), `toString` (sayıdan string'e), `parseInt`/`parseFloat`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Açık Sorular
|
||||||
|
- Builtin'ler kullanıcı tarafından **gölgelenebilir (shadow)** mi — yani kullanıcı kendi `print` fonksiyonunu tanımlarsa ne olur (muhtemelen `E002` çift tanım, ama global scope'ta builtin'lerle çakışma ayrı ele alınmalı)?
|
||||||
|
- Builtin kataloğu büyüdükçe (örn. 50+ fonksiyon), bunların hepsi "her zaman yüklü" mü olacak, yoksa modül sistemi (ayrı issue) ile "import edilen" bir stdlib mantığına mı evrilecek?
|
||||||
|
|
||||||
|
*İmza/Yorum:* `print`'in nasıl çözümlendiğini doğru kurmak, sonraki tüm builtin'lerin şablonu olacak — bu yüzden bu issue erken (Faz 2-3 sırasında) gündeme gelmeli."""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "[Fikir] Minimal Stdlib Seti (String/Math/Koleksiyon Yardımcıları)",
|
||||||
|
"labels": [L_FIKIR, L_FFI],
|
||||||
|
"body": """### Giriş (Nedir, Neden Önemli?)
|
||||||
|
ADR-017 "batteries = sınır problemi" diyor ve JSON/XML/HTML gibi parser'ların saQut'un kendisinde yazılabileceğini söylüyor. Ama bunlar için bile bir **minimal temel** (string birleştirme, karşılaştırma, sayı↔string dönüşümleri, dizi üzerinde gezinme yardımcıları) gerekir. Bu issue, "demo programları yazılabilir olması için gereken en küçük stdlib" kümesini tanımlar.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Gelişme (Olası Yaklaşımlar)
|
||||||
|
- **String:** `length(s)`, `substring(s, start, end)`, `charAt(s, i)`, `concat(a, b)` (veya `+` operatörü), `equals(a, b)`.
|
||||||
|
- **Sayı:** `toString(n)`, `parseInt(s)`, `parseFloat(s)`, `abs`, `min`, `max`.
|
||||||
|
- **Array:** `length(arr)`, belki `append`/`push` (dinamik array tartışmasına bağlı — ayrı issue).
|
||||||
|
- Hepsi builtin kataloğu (önceki issue) üzerinden, ya doğrudan IR opcode'u ya da FFI (`callhost`) ile saç host C++ standart kütüphanesine (`<string>`, `<cmath>`) yaslanır — "asla elle yeniden yazma" (ADR-017, kripto örneğiyle aynı ilke string/math için de geçerli).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Açık Sorular
|
||||||
|
- Bu set "derleyiciye gömülü" mü olmalı (her zaman hazır) yoksa ileride bir "standart modül" (`import std`) olarak mı paketlenmeli — import sistemi (ayrı issue) ile zamanlama ilişkisi var.
|
||||||
|
- JSON parser demo'su (ADR-017'de bahsedilen) için bu listenin yeterli olup olmadığı, demo yazılmaya başlandığında netleşecek.
|
||||||
|
|
||||||
|
*İmza/Yorum:* "Fibonacci çalıştı" milestone'undan sonraki ikinci somut hedef belki de "stdlib ile küçük bir JSON parser demo'su" olabilir — bu issue o hedefin ön koşullarını listeliyor."""
|
||||||
|
},
|
||||||
|
# ---------------- Tooling: LSP / Syntax Highlighting ----------------
|
||||||
|
{
|
||||||
|
"title": "[Fikir] LSP (Language Server Protocol) Sunucusu Mimarisi",
|
||||||
|
"labels": [L_FIKIR, L_TOOLING],
|
||||||
|
"body": """### Giriş (Nedir, Neden Önemli?)
|
||||||
|
**LSP (Language Server Protocol, Dil Sunucusu Protokolü)**, editörlerin (VS Code, Neovim vb.) "kırmızı çizgi göster", "tanıma git", "otomatik tamamla" gibi özellikleri standart bir protokolle derleyiciden alabilmesini sağlar. saQut'un "incelenebilir derleyici" felsefesi, LSP için aslında olağanüstü bir başlangıç noktası: AST, sembol tablosu ve diagnostic'ler zaten JSON olarak dışa açık.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Gelişme (Olası Yaklaşımlar)
|
||||||
|
- LSP sunucusu, saQut derleyicisinin **üstüne** yazılan ayrı bir süreç (process) olabilir — derleyici çekirdeğini bir kütüphane (library) olarak kullanır, stdin/stdout üzerinden JSON-RPC (LSP'nin temel protokolü) ile editörle konuşur.
|
||||||
|
- En kritik performans sorunu: editördeki her tuş vuruşunda **tüm dosyayı yeniden derlemek** pahalıdır. "Incremental parsing" (artımlı ayrıştırma) veya en azından "değişen fonksiyonu yeniden derle" gibi bir strateji gerekir — ama bu, v0'ın çok ilerisinde bir konu.
|
||||||
|
- "Önce dikey dilim": LSP'nin ilk versiyonu sadece `DiagnosticEngine`'in (Faz 0) çıktısını "hover/error squiggle" olarak göstermekle başlayabilir — tüm dosyayı her kaydette yeniden derlemek v0 için yeterli olabilir.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Açık Sorular
|
||||||
|
- LSP sunucusu aynı C++ binary'sinin bir modu mu (`saqut lsp`) olacak, yoksa tamamen ayrı bir proje mi?
|
||||||
|
- "Tanıma git" (go-to-definition) özelliği doğrudan Faz 2'nin Symbol Table'ındaki `definitionLoc`/`references` alanlarından besleniyor — bu alanların LSP ihtiyaçlarına göre yeterli olup olmadığı (örn. cross-file references, modül sistemi geldiğinde) kontrol edilmeli.
|
||||||
|
|
||||||
|
*İmza/Yorum:* "Derleyici tamamlandıktan sonra" diye düşünülecek bir konu ama Faz 2'nin sembol tablosu tasarımı şimdiden bunu destekleyecek şekilde (her referansın konumu kayıtlı) kurulursa, LSP'ye geçiş çok daha az acı verir."""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "[Fikir] Syntax Highlighting (Sözdizimi Renklendirme) Grameri",
|
||||||
|
"labels": [L_FIKIR, L_TOOLING],
|
||||||
|
"body": """### Giriş (Nedir, Neden Önemli?)
|
||||||
|
Editörlerde `.sqt` dosyalarının renkli görünmesi (anahtar kelimeler, string'ler, yorumlar farklı renkte) geliştirici deneyiminin en temel parçalarından biri — ve LSP'den bağımsız, çok daha basit bir kazanım.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Gelişme (Olası Yaklaşımlar)
|
||||||
|
- **TextMate grameri** (VS Code'un kullandığı, regex-tabanlı `.tmLanguage.json`): saQut'un tokenizer'ındaki (Tokenizer) 6 token tipinden (`docs` belgelerine göre) yola çıkarak basit bir regex seti yazılabilir — derleyiciden bağımsız, statik bir dosya.
|
||||||
|
- **Tree-sitter grameri:** Daha güçlü (gerçek bir parser üretir, hata-toleranslı), ama saQut'un kendi Pratt parser'ından ayrı bir gramer tanımı gerektirir — bakım yükü iki ayrı "dil tanımı" demektir.
|
||||||
|
- v0 önerisi: TextMate grameri (en düşük efor, en hızlı görsel kazanım) — `saqut tokens` çıktısı referans alınarak anahtar kelimeler/operatörler/literal'ler renklendirilir.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Açık Sorular
|
||||||
|
- Bu gramer, tokenizer'daki token tanımları değiştiğinde **elle senkronize** mi tutulacak, yoksa `saqut tokens --dump-grammar` gibi bir komutla **otomatik üretilebilir** mi (programlanabilir derleyici felsefesiyle çok uyumlu bir fikir)?
|
||||||
|
- Hedef editör önceliği ne olacak — VS Code (TextMate) mı, Neovim/Helix (Tree-sitter ekosistemi daha güçlü) mı?
|
||||||
|
|
||||||
|
*İmza/Yorum:* Bu, listedeki **en düşük efor / en yüksek "keyif" oranına** sahip issue'lardan biri — derleyici bitmeden bile, sadece tokenizer çıktısından üretilebilir."""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "[Fikir] `saqut fmt` — Otomatik Kod Biçimlendirici (Formatter)",
|
||||||
|
"labels": [L_FIKIR, L_TOOLING],
|
||||||
|
"body": """### Giriş (Nedir, Neden Önemli?)
|
||||||
|
`gofmt`, `rustfmt`, `prettier` gibi araçlar artık modern bir dilin "olmazsa olmaz"ı haline geldi: kod her zaman aynı şekilde biçimlenir, "stil tartışmaları" ortadan kalkar. saQut'un AST'si zaten JSON'a serileştirilebiliyor — bu, bir formatter için iyi bir temel.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Gelişme (Olası Yaklaşımlar)
|
||||||
|
- `saqut fmt file:kaynak.sqt`: dosyayı ayrıştırır (AST), AST'yi **kanonik bir şekilde** tekrar kaynak koda yazdırır (pretty-printer / AST → metin).
|
||||||
|
- Yorumların (comment) korunması en zor kısım — AST yorum bilgisini taşımıyorsa (Faz 0-1 sırasında bu netleşmeli), formatter yorumları silebilir veya yanlış yere koyabilir. Bu, **AST tasarımına şimdiden not edilmesi gereken bir kısıt.**
|
||||||
|
- "Öncesi/sonrası" felsefesiyle: `saqut fmt --check` (CI'da kullanılabilir — "bu dosya zaten formatlı mı?") ve `saqut fmt --write` (dosyayı yerinde değiştir) ayrımı yapılabilir.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Açık Sorular
|
||||||
|
- Pretty-printer, AST'den mi yoksa doğrudan token akışından mı (yorumlar dahil tüm token'lar tokenizer'da zaten tutuluyor) üretilmeli — token-tabanlı yaklaşım yorum-koruma sorununu çözebilir.
|
||||||
|
- Biçimlendirme kuralları (girinti, boşluk, satır uzunluğu) nasıl ve nerede sabitlenecek — `docs/` altında bir "stil kılavuzu" mu yazılacak?
|
||||||
|
|
||||||
|
*İmza/Yorum:* AST tasarımı (Faz 1) sırasında "yorumlar AST'de nasıl temsil edilir" sorusu bu issue'yu doğrudan etkiler — şimdiden not düşülmeye değer bir bağımlılık."""
|
||||||
|
},
|
||||||
|
# ---------------- Gelecek Vizyonu / Deneysel ----------------
|
||||||
|
{
|
||||||
|
"title": "[Fikir] Zaman-Yolculuğu Hata Ayıklama (Time-Travel Debugging) — IR Anlık Görüntüleri",
|
||||||
|
"labels": [L_FIKIR, L_VIZYON],
|
||||||
|
"body": """### Giriş (Nedir, Neden Önemli?)
|
||||||
|
saQut'un "her aşama incelenebilir" felsefesi VM çalışma zamanına da taşınabilir: VM her IR komutunu çalıştırdığında **tüm değişken/yığın durumunun anlık görüntüsünü (snapshot)** kaydederse, kullanıcı programı "ileri-geri sarabilir" — modern debugger'larda (rr, Chrome DevTools "time travel") popüler ama çoğu küçük dilde olmayan bir özellik.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Gelişme (Olası Yaklaşımlar)
|
||||||
|
- VM'in yorumlayıcı döngüsüne (ilgili issue) bir "trace modu" eklenir: her adımda (komut, önceki durum, sonraki durum) bir listeye/dosyaya yazılır.
|
||||||
|
- `saqut run file --trace=trace.json` → `saqut replay trace.json` gibi bir akış: kullanıcı adım adım ileri/geri gidebilir, herhangi bir adımdaki değişken değerlerini görebilir.
|
||||||
|
- Bellek modeli basit olduğu için (host heap, özel allocator yok — ADR-015) snapshot almak büyük bir teknik engel değil; asıl maliyet disk/bellek kullanımıdır (büyük döngülerde trace çok büyüyebilir — örnekleme/limit gerekebilir).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Açık Sorular
|
||||||
|
- Trace formatı IR serileştirmesiyle (ilgili issue) aynı JSON şemasını mı paylaşmalı?
|
||||||
|
- Bu özellik "VM v1"in parçası mı, yoksa tamamen ayrı bir "saqut debug" aracı mı?
|
||||||
|
|
||||||
|
*İmza/Yorum:* Bu, "alet çantası" felsefesinin VM çalışma zamanına en doğal uzantısı — diğer küçük dillerde nadiren bulunan, saQut'u **ayırt edici** kılabilecek bir özellik."""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "[Fikir] Derleyici Playground — WebAssembly'e Derleyip Tarayıcıda Çalıştırma",
|
||||||
|
"labels": [L_FIKIR, L_VIZYON],
|
||||||
|
"body": """### Giriş (Nedir, Neden Önemli?)
|
||||||
|
Rust/Go/TypeScript gibi dillerin "playground" siteleri (tarayıcıda kod yaz, çalıştır, AST/derleme adımlarını gör) hem öğretim hem de tanıtım için çok güçlü bir araç. saQut'un C++ çekirdeği **WebAssembly (Wasm)**'a derlenebilirse (Emscripten ile), tüm derleyici tarayıcıda çalışabilir — sunucu gerekmez.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Gelişme (Olası Yaklaşımlar)
|
||||||
|
- Emscripten ile `saqut` binary'si bir `.wasm` + JS "glue" koduna derlenir. Web sayfası bu Wasm modülünü yükler, kullanıcı kodunu derleyiciye verir.
|
||||||
|
- saQut'un CLI komutları (`tokens`, `ast`, `symbols`, ileride `ir`, `run`) zaten JSON döndürdüğü için, web arayüzü bu JSON'ları görselleştirir — örneğin AST'yi bir ağaç diyagramı olarak çizmek.
|
||||||
|
- "Önce dikey dilim, sonra çerçeve" ilkesiyle: bu issue, derleyicinin **kendisi** tamamlanmadan ele alınmaması gereken, ama CMake yapılandırmasına (ADR-003, header-only) Wasm hedefinin **erkenden uyumlu** olup olmadığının kontrol edilmesi gereken bir konudur.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Açık Sorular
|
||||||
|
- Wasm derlemesi CI'a (varsa) eklenir mi — "her commit'te playground güncel" gibi bir otomasyon hedefi olur mu?
|
||||||
|
- Playground sadece görüntüleme mi yapar, yoksa `saqut run` ile gerçek **çalıştırma** da (VM tamamlandıktan sonra) tarayıcıda mümkün olur mu?
|
||||||
|
|
||||||
|
*İmza/Yorum:* "Keyif veren" kategorisinin tipik örneği — teknik olarak büyük bir engel yok (C++ → Wasm olgun bir yol), ama önceliği derleyici çekirdeğinden sonra gelmeli."""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "[Fikir] Dil-İçi Test Bloğu (`test { }`) ve Yerleşik Test Çalıştırıcı",
|
||||||
|
"labels": [L_FIKIR, L_VIZYON],
|
||||||
|
"body": """### Giriş (Nedir, Neden Önemli?)
|
||||||
|
Rust'ın `#[test]` ve Go'nun `_test.go` dosyaları gibi, modern diller test yazmayı dilin/araç zincirinin **bir parçası** haline getiriyor. saQut için de "fonksiyonumu yazdım, hemen yanına küçük bir test ekleyeyim" akışı düşünülebilir — derleyicinin kendi `examples/fibonacci.sqt` gibi referans programlarını doğrulaması için bile yararlı olur.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Gelişme (Olası Yaklaşımlar)
|
||||||
|
- Sözdizimi fikri: `test "fibonacci(5) dogru sonucu verir" { assert(fibonacci(5) == 5); }` — `test` ve `assert` yeni anahtar kelimeler/builtin'ler olarak eklenir.
|
||||||
|
- `saqut test file:kaynak.sqt`: dosyadaki tüm `test` bloklarını çalıştırır, `assert` başarısız olursa hangi test/satır olduğunu raporlar (DiagnosticEngine'in bir "test sonucu" varyantı gibi düşünülebilir).
|
||||||
|
- Bu özellik aslında `assert` builtin'i (FFI/builtin kataloğu issue'suyla ilişkili) + basit bir "bu bloğu normal akıştan ayrı çalıştır" kuralı kadar küçük olabilir — büyük bir çerçeveye gerek yok.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Açık Sorular
|
||||||
|
- `test` blokları normal derlemede (`saqut run`) tamamen yok sayılır mı (production kodundan ayrışma), yoksa özel bir bayrakla mı dahil edilir?
|
||||||
|
- Bu, dilin **sözdizimine** mi (yeni anahtar kelime) yoksa sadece bir **kütüphane fonksiyonu** (`assert(...)` + isim kuralına dayalı dosya/fonksiyon tarama) düzeyinde mi kalmalı — ikincisi dil kimliğini (kilitli kararlar) bozmaz.
|
||||||
|
|
||||||
|
*İmza/Yorum:* "Günümüzde frameworklerde olan ama derleyiciye gömülü gelebilecek" taleplere iyi bir örnek — minimal versiyonu (`assert` builtin'i) çok düşük maliyetli."""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "[Fikir] Paket Yöneticisi / Modül Kayıt Defteri (Registry) Vizyonu",
|
||||||
|
"labels": [L_FIKIR, L_VIZYON],
|
||||||
|
"body": """### Giriş (Nedir, Neden Önemli?)
|
||||||
|
Modül sistemi (ayrı issue) "aynı projede birden fazla dosya" sorusunu çözer. Ama "başka birinin yazdığı bir saQut kütüphanesini projeme nasıl eklerim" sorusu bir **paket yöneticisi** gerektirir (npm, cargo, go modules tarzı). Bu, şu an çok uzak bir vizyon ama erken not edilmeye değer.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Gelişme (Olası Yaklaşımlar)
|
||||||
|
- En minimal hali: `import "https://git.saqut.com/.../util.sqt"` gibi URL-tabanlı import (Go'nun ilk modül modeline benzer) — merkezi bir registry gerektirmez, ADR-017'deki "FFI/kütüphane sınırı" felsefesiyle uyumlu (saQut çekirdeği paket yönetimini bilmez, sadece dosya/URL okur).
|
||||||
|
- Daha "gerçek" hali: `saqut.toml` gibi bir manifest dosyası, bağımlılık sürümleri, kilit dosyası (lock file) — bu, derleyici çekirdeğinden tamamen ayrı bir CLI aracı (`saqut-pkg`) olarak düşünülebilir, çekirdeği şişirmez.
|
||||||
|
- saQut'un "alet çantası" kimliği burada bir fırsat sunuyor: paket yöneticisi, saQut'un kendisiyle yazılabilecek bir **dogfooding** (kendi aracını kendi diliyle yazma) projesi olabilir — ama bu, dilin dosya I/O ve string işleme (stdlib issue'ları) yeterince olgunlaştıktan sonra mümkün.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Açık Sorular
|
||||||
|
- Bu vizyon, projenin "küçük/incelenebilir alet çantası" kimliğiyle ne kadar uyumlu — paket ekosistemi büyüdükçe "monolit korkusu" (ADR-017) geri gelir mi?
|
||||||
|
- Merkezi bir registry (sunucu, hosting maliyeti) gerçekten gerekli mi, yoksa Git-tabanlı/URL-tabanlı dağıtık model yeterli mi?
|
||||||
|
|
||||||
|
*İmza/Yorum:* En "uzak gelecek" issue'lardan biri — ama "import sistemi" tasarlanırken (örn. URL desteği baştan düşünülürse mi, sonradan mı eklenir) bu vizyonun gölgesi düşebilir, o yüzden şimdiden not."""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "[Fikir] Akıllı Diagnostic — Hata Mesajlarına \"Neden\" ve \"Nasıl Düzeltilir\" Ekleme",
|
||||||
|
"labels": [L_FIKIR, L_VIZYON],
|
||||||
|
"body": """### Giriş (Nedir, Neden Önemli?)
|
||||||
|
Faz 0'ın `Diagnostic` yapısında zaten bir `hint` (ipucu) alanı planlanıyor. Bu issue, o alanın **nasıl** dolduğunu ve modern derleyicilerin (Rust, Elm) "hata mesajı bir öğretmen gibi konuşur" felsefesini saQut'a nasıl taşıyabileceğimizi tartışır — yapay zeka kullanmadan, tamamen kural-tabanlı (rule-based).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Gelişme (Olası Yaklaşımlar)
|
||||||
|
- Her hata kodu (`E001`...`E010`) için, hata mesajına ek olarak **statik bir "neden" şablonu** ve mümkünse **"şunu dener misin"** önerisi eklenir. Örnek: `E003` (tip uyuşmazlığı) → "saQut'ta değişkenler arası gizli dönüşüm yapılmaz (bkz. ADR-010); `int` bir değeri `float` değişkene atamak için ... yapabilirsiniz."
|
||||||
|
- Bu şablonlar `docs/adr-frontend-analiz.md`'deki kararlarla **doğrudan bağlantılı** olabilir — hata mesajı, ilgili ADR'nin "neden"ini özetleyen bir cümle içerebilir. Bu, dökümantasyon ile derleyici çıktısını birbirine bağlar (saQut'un "her şey ilişkili ve incelenebilir" ruhuna uygun).
|
||||||
|
- Elm tarzı "did you mean...?" (yazım hatası önerisi): tanımsız isim hatasında (`E001`), sembol tablosundaki (Faz 2) benzer isimli sembollere Levenshtein-mesafesi gibi basit bir algoritmla öneri sunulabilir — küçük, eğlenceli, yüksek-etkili bir özellik.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Açık Sorular
|
||||||
|
- Bu zenginleştirilmiş mesajlar `DiagnosticEngine`'in (Faz 0) temel yapısına mı gömülür, yoksa ayrı bir "mesaj kataloğu" dosyası mı olur (çeviri/yerelleştirme ihtimaline de açık kapı bırakır)?
|
||||||
|
- "Did you mean" önerisi performans maliyeti yaratır mı (her hata için sembol tablosu taraması) — büyük projelerde sorun olur mu?
|
||||||
|
|
||||||
|
*İmza/Yorum:* Bu, derleyicinin "kullanıcıyla konuşma tarzını" belirleyen, teknik olarak küçük ama **algıyı** çok değiştirebilecek bir issue — Faz 0 bittiğinde, kataloğa hint şablonları eklemek ucuz bir ek iştir."""
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
for issue in issues_data:
|
||||||
|
endpoint = f"{REPO_PATH}/issues"
|
||||||
|
res = make_request(endpoint, method="POST", data=issue)
|
||||||
|
print(f"Created issue #{res['number']}: {res['title']}")
|
||||||
|
|
@ -0,0 +1,160 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
import sys
|
||||||
|
from gitea import make_request, REPO_PATH
|
||||||
|
|
||||||
|
issues_data = [
|
||||||
|
{
|
||||||
|
"title": "Faz 0 — Tip Sistemi ve Hata Kayıt Motoru (Type & Diagnostics)",
|
||||||
|
"body": """### Giriş (Nedir, Neden Yapıyoruz?)
|
||||||
|
Bu iş paketinde derleyicinin çalışması için en temel iki altyapıyı kuracağız: Tip Sistemi (Type System) ve Hata Kayıt/Diagnostic Motoru.
|
||||||
|
Tip Sistemi, derleyicinin kaynak koddaki verileri (sayılar, metinler vb.) anlamlandırabilmesi ve tür doğruluğunu denetleyebilmesi için gerekli veri yapısını sağlar.
|
||||||
|
Hata Kayıt Motoru ise derleme sırasında oluşan hataları (örneğin tip uyuşmazlığı veya tanımsız değişken kullanımı) biriktirip, kullanıcıya hatanın kaynak koddaki tam yerini (dosya, satır, sütun koordinatları) göstererek raporlayan mekanizmadır. Bu yapı sayesinde derleyici ilk bulduğu hatada hemen durmayacak, tüm hataları toplayıp tek seferde kullanıcıya sunacaktır.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Gelişme (Neyi, Nerede ve Nasıl Yapacaksın?)
|
||||||
|
Aşağıdaki adımları sırasıyla gerçekleştir:
|
||||||
|
1. **Tip Yapısı:** `src/core/type.hpp` adında yeni bir dosya oluştur. Burada `TypeKind` (Primitive, Array, Struct, Function, Error) enum sınıfını ve `Type` sınıfını tanımla. Primitif türler için `int`, `float`, `double`, `char`, `string`, `bool`, `void` alt tiplerini destekle. Dizi (Array) türü için eleman tipini (`elementType`), fonksiyon için dönüş ve parametre tiplerini taşıyacak alanlar ekle. `equals()` ve `toString()` gibi metotları implement et.
|
||||||
|
2. **Hata Yapısı:** `src/diagnostic/diagnostic.hpp` dosyasını oluştur. Burada hata seviyelerini (`Error`, `Warning`, `Note`, `Hint`) içeren `DiagLevel` enum sınıfını ve hatanın koordinatlarını tutan `Diagnostic` yapısını tanımla.
|
||||||
|
3. **Diagnostic Kataloğu:** Aynı dosyada derleyicide oluşabilecek hata kodlarını içeren kataloğu (örneğin `E001` - Tanımsız Değişken, `E002` - Çift Tanım, `E003` - Tip Uyuşmazlığı, `E010` - Döngüsel Struct Tanımı vb.) sabitle.
|
||||||
|
4. **DiagnosticEngine:** `src/diagnostic/diagnostic_engine.hpp` dosyasını oluştur. `DiagnosticEngine` sınıfı hataları bir vektörde biriktirmeli, `report()` metoduyla yeni hata almalı ve `printAll()` ile bunları ekrana formatlı yazmalıdır.
|
||||||
|
|
||||||
|
*Geliştirme Dalı (Branch):* `feature/faz0-temeller`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Sonuç ve Başarı Kriterleri
|
||||||
|
Bu işin bittiğini ve başarılı olduğunu şu kriterlerle doğrulayacağız:
|
||||||
|
1. `Type::equals()` metodu için yazılan birim testler (Unit Tests) farklı tip kombinasyonlarını (örneğin `int` ile `int` eşit, `int` ile `float` veya `int[]` farklı) hatasız doğrulamalıdır.
|
||||||
|
2. `DiagnosticEngine` sınıfına 3 farklı hata raporlanıp `printAll()` çağrıldığında, çıktıda hata kodları (`E001` vb.) ve kaynak kod satır/sütun koordinatları doğru sırayla ve biçimde ekrana yazılmalıdır.
|
||||||
|
3. Derleyici uyarısız (`-Wall -Wextra`) derlenmelidir.
|
||||||
|
|
||||||
|
*Mühendis Olmayanlar İçin Analiz:* Bu aşama derleyicinin arka planındaki veri yapılarını kurduğu için görsel bir çıktı üretmez, ancak sonraki tüm aşamaların temelidir. Karmaşıklığı **Düşük**, yapım süresi yaklaşık **1-2 gün**dür.
|
||||||
|
*İmza/Yorum:* Bu altyapı semantik analizin (Faz 3) hata raporlayabilmesi için kritik önkoşuldur."""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Faz 1 — AST Hiyerarşisinin Refaktörü (Expression ve Statement Ayrımı)",
|
||||||
|
"body": """### Giriş (Nedir, Neden Yapıyoruz?)
|
||||||
|
Bu iş paketinde, derleyicinin kaynak kodu okuduktan sonra hafızada oluşturduğu Soyut Sözdizimi Ağacı (Abstract Syntax Tree - AST) düğümlerini yeniden yapılandıracağız.
|
||||||
|
Şu anda tüm AST düğümleri tek bir `ASTNode` taban sınıfından türemektedir. Ancak anlamsal olarak iki farklı düğüm türü vardır:
|
||||||
|
1. **Expression (İfade):** Bir değer üreten yapılar (örneğin `5 + 3` veya değişken isimleri). Bunların bir veri tipi (Type) olur.
|
||||||
|
2. **Statement (Deyim):** Bir değer üretmeyen, sadece bir iş/eylem yürüten kontrol yapıları (örneğin `if` bloğu, döngüler veya `return`). Bunların tipi yoktur ancak erişilebilirlik (reachability) durumları analiz edilir.
|
||||||
|
Bu iki yapıyı birbirinden ayırmak, sonraki aşamada tip denetimi yapabilmemiz için zorunludur.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Gelişme (Neyi, Nerede ve Nasıl Yapacaksın?)
|
||||||
|
Aşağıdaki adımları sırasıyla gerçekleştir:
|
||||||
|
1. **Ara Taban Sınıflarını Ekle:** `src/parser/ast_node.hpp` dosyasına `ExpressionNode` ve `StatementNode` adında iki yeni sınıf ekle. `ExpressionNode` sınıfına `resolvedType` (çözümlenmiş tip), `isConstant` ve `foldedValue` (optimizasyon için) alanlarını koy. `StatementNode` sınıfına ise `isReachable` (erişilebilirlik bayrağı) alanını ekle.
|
||||||
|
2. **Düğümleri Güncelle:** `src/parser/nodes/` dizinindeki tüm dosyaları tara. `LiteralNode`, `BinaryExpressionNode`, `IdentifierNode`, `CallExpressionNode` gibi değer üreten tüm sınıfları `ExpressionNode`'dan türet. `IfStatementNode`, `WhileStatementNode`, `ReturnStatementNode` vb. sınıfları ise `StatementNode`'dan türet.
|
||||||
|
3. **Çocuk Gezintisini Düzelt:** AST taban sınıfındaki `getChildren()` metodunun alt sınıflar tarafından ezilmesini (override) sağla. Örneğin `BinaryExpressionNode` sınıfında `Left` ve `Right` düğümlerini dinamik olarak `getChildren()` içinde döndür. Bu, genel ağaç gezginlerinin (Tree Walker) alt düğümleri eksiksiz taramasını sağlayacaktır.
|
||||||
|
4. **toJson Modifikasyonu:** `toJson()` metotlarını güncelleyerek yeni alanları (çözümlenmiş tip ve erişilebilirlik bilgileri) JSON çıktısına ekle.
|
||||||
|
|
||||||
|
*Geliştirme Dalı (Branch):* `feature/faz1-ast-refactor`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Sonuç ve Başarı Kriterleri
|
||||||
|
Bu işin bittiğini ve başarılı olduğunu şu kriterlerle doğrulayacağız:
|
||||||
|
1. `saqut ast examples/fibonacci.sqt` komutu çalıştırıldığında üretilen JSON çıktısında, ifadeler için `resolvedType` alanı (boş olarak) ve deyimler için `isReachable` alanı (true olarak) görünmelidir.
|
||||||
|
2. JSON yapısının doğruluğu `python3 -m json.tool` ile test edildiğinde hiçbir parse hatası vermebelidir (regresyon testi).
|
||||||
|
3. Tüm kodlar uyarısız derlenmelidir.
|
||||||
|
|
||||||
|
*Mühendis Olmayanlar İçin Analiz:* Bu aşama mevcut veri yapısını daha düzenli hale getirir. Dışarıdan bakıldığında JSON çıktısında yeni alanlar görülür. Karmaşıklığı **Düşük-Orta**, yapım süresi yaklaşık **2 gün**dür.
|
||||||
|
*İmza/Yorum:* Düğüm alanlarındaki `children` vektörü tutarsızlığı bu fazda tamamen çözülmeli, ağaçta yukarı ve aşağı gezinme API'si garanti altına alınmalıdır."""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Faz 2 — Sembol Tablosu ve İki Geçişli Toplama (Scope & Symbol Table)",
|
||||||
|
"body": """### Giriş (Nedir, Neden Yapıyoruz?)
|
||||||
|
Bu iş paketinde derleyicinin \"Hafızası\" sayılan Sembol Tablosunu (Symbol Table) kuracağız.
|
||||||
|
Sembol Tablosu, kaynak koddaki değişkenlerin, fonksiyonların ve veri yapılarının (struct) tanımlarını, veri tiplerini, hangi kapsamlarda (Scope) geçerli olduklarını ve kodun neresinde kullanıldıklarını (referanslarını) takip eden bir veri yapısıdır.
|
||||||
|
Dilimiz, tanımlanmadan önce fonksiyon çağrılmasına izin verdiği için (ileri başvuru / forward-reference), sembolleri toplama işlemini iki geçişli bir algoritma ile yapmak zorundayız.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Gelişme (Neyi, Nerede ve Nasıl Yapacaksın?)
|
||||||
|
Aşağıdaki adımları sırasıyla gerçekleştir:
|
||||||
|
1. **Sembol ve Kapsam Yapıları:** `src/symbol/` adında yeni bir dizin aç. Burada `Symbol` yapısını tanımla (ad, tür, tip, tanım konumu, referans listesi alanları bulunsun). `Scope` sınıfını iç içe geçebilir ağaç yapısında yaz; her scope bir üst scope'a pointer tutsun.
|
||||||
|
2. **SymbolTable Yöneticisi:** `src/symbol/symbol_table.hpp` dosyasını oluştur. Scope yığınını yöneten `enterScope()`, `exitScope()`, sembol ekleyen `define()` (aynı scope'ta çift tanım varsa hata üret) ve içten dışa arayan `resolve()` metotlarını yaz.
|
||||||
|
3. **İki Geçişli Toplayıcı:** `src/symbol/symbol_collector.hpp` ve `.cpp` dosyalarını oluştur.
|
||||||
|
* **Geçiş 1 (Global Hoisting):** AST'yi tarayarak sadece üst seviye fonksiyon ve struct imzalarını global scope'a kaydet.
|
||||||
|
* **Geçiş 2 (Local Resolution):** Fonksiyon gövdelerine in. Lokal değişkenleri declare-before-use (kullanmadan önce tanımlama) kuralıyla topla ve Identifier (isim referansı) düğümlerini sembol tablosundaki sembollerle bağla. Global değişken başlatıcılarında declare-before-use kuralını zorunlu kıl.
|
||||||
|
4. **Döngüsel Struct Kontrolü:** Struct tanımları toplandıktan sonra, pointer olmadığı için birbirini değer olarak içeren döngüsel struct tanımlarını (örn. `struct A { B b; }` ve `struct B { A a; }`) DFS (Derinlik Öncelikli Arama) algoritmasıyla tespit et ve `E010` hatasını raporla.
|
||||||
|
|
||||||
|
*Geliştirme Dalı (Branch):* `feature/faz2-symbols`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Sonuç ve Başarı Kriterleri
|
||||||
|
Bu işin bittiğini ve başarılı olduğunu şu kriterlerle doğrulayacağız:
|
||||||
|
1. `saqut symbols examples/fibonacci.sqt` komutu çalıştırıldığında, programdaki fonksiyonlar, parametreler ve lokal değişkenler doğru tipleri ve tüm kullanım koordinatlarıyla (satır/sütun referans listesi) ekrana yazılmalıdır.
|
||||||
|
2. Aynı isimde iki değişken tanımlandığında `E002` (Çift Tanım), tanımsız bir değişken çağrıldığında veya global başlatıcı sırasında ileri başvuru yapıldığında `E001` (Tanımsız İsim), döngüsel struct tanımlandığında ise `E010` hataları DiagnosticEngine tarafından raporlanmalı ve derleme durmalıdır.
|
||||||
|
|
||||||
|
*Mühendis Olmayanlar İçin Analiz:* Derleyicinin değişken ve fonksiyon isimlerini anlamlandırmaya başladığı aşamadır. Hatalı kod yazıldığında derleyicinin verdiği akıllı uyarıların temelidir. Karmaşıklığı **Yüksek**, yapım süresi yaklaşık **4-5 gün**dür.
|
||||||
|
*İmza/Yorum:* Bu aşamada pointer ve scope yapıları çok hassas tasarlanmalıdır; bellek sızıntılarını önlemek için akıllı işaretçiler veya net sahiplik modelleri tercih edilmelidir."""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Faz 3 — Semantik Analiz ve Tip Denetimi (Type Checking)",
|
||||||
|
"body": """### Giriş (Nedir, Neden Yapıyoruz?)
|
||||||
|
Bu iş paketinde derleyicinin kurallarını işleten Semantik Analiz (Anlamsal Analiz) ve Tip Denetimi (Type Checking) motorunu yazacağız.
|
||||||
|
Sözdizimi (Syntax) doğru olan bir kod anlamsal olarak yanlış olabilir (örneğin `int x = \"merhaba\" + 5;` yazmak sözdizimsel olarak geçerlidir ancak mantıksızdır).
|
||||||
|
Tip Denetimi, ifadelerin veri tiplerinin uyumlu olup olmadığını kontrol eder. Ayrıca dilimizin \"otomatik tip dönüşümü yasaktır\" kuralını işletir. Yapısal doğrulama ise `break`/`continue` deyimlerinin döngü dışında kullanılmasını engeller.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Gelişme (Neyi, Nerede ve Nasıl Yapacaksın?)
|
||||||
|
Aşağıdaki adımları sırasıyla gerçekleştir:
|
||||||
|
1. **Tip Denetleyici:** `src/sema/type_checker.hpp` ve `.cpp` dosyalarını oluştur. AST'yi aşağıdan yukarıya (bottom-up) gezerek her `ExpressionNode` düğümünün tipini (`resolvedType`) belirle.
|
||||||
|
2. **Uyuşmazlık Kuralları:** Atama işlemlerinde (`=`), aritmetik operatörlerde ve fonksiyon çağrılarında tip uyumunu denetle. Gizli değişken-değişken dönüşümlerini yasakla (hata: `E003`).
|
||||||
|
3. **Literal Uyarlama Kuralı:** Sabit sayı literal'ları için bağlama göre tiplendirme kuralını işlet (Örn: `float x = 1;` geçerli olmalı çünkü `1` kayıpsızca float'a dönüşebilir, ancak `int y = 1.5;` `E003` hatası vermelidir).
|
||||||
|
4. **Error Tipi:** Bir ifade hatalıysa tipine `ErrorType` ata. Bu sayede, o ifadeyi kullanan üst ifadelerde tekrar tekrar sahte hatalar üretilmez.
|
||||||
|
5. **Yapısal Doğrulayıcı:** `src/sema/structural_validator.hpp` ve `.cpp` dosyalarını oluştur. Ağacı tırmanarak `break`/`continue` ifadelerinin üstünde bir döngü olup olmadığını (`E004`), `return` ifadesinin bir fonksiyon gövdesinde olup olmadığını (`E005` ve dönüş tipi uyumu için `E006`) denetle.
|
||||||
|
|
||||||
|
*Geliştirme Dalı (Branch):* `feature/faz3-semantic-analysis`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Sonuç ve Başarı Kriterleri
|
||||||
|
Bu işin bittiğini ve başarılı olduğunu şu kriterlerle doğrulayacağız:
|
||||||
|
1. Geçerli olan `examples/fibonacci.sqt` dosyası semantik analizden sıfır hatayla geçmeli ve her AST ifadesinin tipi JSON çıktısında (`resolvedType`) görünmelidir.
|
||||||
|
2. Hatalı bir test dosyası verildiğinde (örneğin döngü dışında `break;` yazıldığında veya `int` değişkene `string` atandığında) derleyici ilgili hata kodlarını (`E003`, `E004` vb.) raporlamalı ve derleme süreci kesilmelidir.
|
||||||
|
|
||||||
|
*Mühendis Olmayanlar İçin Analiz:* Bu aşama, derleyicinin yazılan kodun \"anlamlı\" olup olmadığını kontrol ettiği güvenlik kapısıdır. Karmaşıklığı **Orta-Yüksek**, yapım süresi yaklaşık **3-4 gün**dür.
|
||||||
|
*İmza/Yorum:* Sabit ifadelerde constant folding istisnasını unutmama kuralları burada işletilecektir."""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Faz 4 — Optimizasyon Altyapısı ve Kaynak Optimizasyonları (Optimization)",
|
||||||
|
"body": """### Giriş (Nedir, Neden Yapıyoruz?)
|
||||||
|
Bu iş paketinde derleyicinin ürettiği kodu daha verimli hale getiren Optimizasyon (Eniyileme) altyapısını ve ilk optimizasyon adımlarını (pass'leri) yazacağız.
|
||||||
|
Optimizasyon, yazılan kodun anlamını değiştirmeden daha hızlı çalışacak veya daha az yer kaplayacak şekilde dönüştürülmesidir.
|
||||||
|
saQut'un \"alet çantası\" felsefesi gereği, optimizasyon orijinal kaynak kod görüntüsünü bozmamalıdır. Bu nedenle optimizasyonlar AST'nin bir kopyası (klon) üzerinde gerçekleştirilir.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Gelişme (Neyi, Nerede ve Nasıl Yapacaksın?)
|
||||||
|
Aşağıdaki adımları sırasıyla gerçekleştir:
|
||||||
|
1. **Optimizasyon Yöneticisi:** `src/opt/` dizinini oluştur. Soyut `OptimizationPass` sınıfını tanımla. `OptimizationManager` sınıfını yaz; bu sınıf `CompilerConfig` üzerinden aktif edilen pass'leri sırayla çalıştırsın.
|
||||||
|
2. **Fixpoint Döngüsü:** Pass'lerin birbirini tetiklemesi için (örn. folding sonrası ölü kod oluşması) optimizasyonları bir fixpoint döngüsünde (hiçbir pass değişiklik yapmayana kadar) çalıştır. Monoton olmayan pass'lerin sonsuz döngüye girmemesi için sert bir tur sınırı (`maxFixpointRounds`) ekle.
|
||||||
|
3. **Ağaç Klonlama ve Remap:** `ASTNode::clone()` metodunu tüm AST düğümleri için yaz. Kopyalanan düğümlerin `parent` pointer'larını yeni ağaca göre bağla. Klonlanan ağaçtaki Identifier (isim referansı) düğümlerinin sembol tablosu bağlarını, klonlanmış yeni bir sembol tablosuna yönlendir (Remapping).
|
||||||
|
4. **Constant Folding Pass:** `src/opt/constant_folding.hpp` dosyasını oluştur. İki sabit terimin işlemini derleme zamanında hesapla (Örn: `1 + 2` → `3`). Sıfıra bölme varsa katlama yapma, `W002` uyarısı ver.
|
||||||
|
5. **Dead Code Elimination (DCE) Pass:** `src/opt/dead_code_elim.hpp` dosyasını oluştur. Erişilemez deyimleri (örn. `return` sonrası kodlar, `if(false)` gövdeleri) ve hiç kullanılmayan lokal değişkenleri (`W001`) sil.
|
||||||
|
|
||||||
|
*Geliştirme Dalı (Branch):* `feature/faz4-optimization`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Sonuç ve Başarı Kriterleri
|
||||||
|
Bu işin bittiğini ve başarılı olduğunu şu kriterlerle doğrulayacağız:
|
||||||
|
1. `saqut ast examples/fibonacci.sqt --optimized` komutu çalıştırıldığında sabit ifadelerin katlandığı ve varsa ölü kodların silindiği doğrulanmalıdır.
|
||||||
|
2. `--optimized` bayrağı verilmeden çağrıldığında orijinal (optimize edilmemiş) AST çıktısı elde edilmelidir (Öncesi/Sonrası karşılaştırması).
|
||||||
|
3. Folding ve DCE işlemlerinin ardışık olarak birbirini beslediği (folding sonucu oluşan ölü kodun DCE tarafından temizlendiği) zincirleme senaryolar tek bir komutla doğrulanmalıdır.
|
||||||
|
|
||||||
|
*Mühendis Olmayanlar İçin Analiz:* Yazdığınız programın gereksiz kısımlarını temizleyen akıllı temizlikçi aşamasıdır. Kodun boyutunu küçültür. Karmaşıklığı **Yüksek**, yapım süresi yaklaşık **4-5 gün**dür.
|
||||||
|
*İmza/Yorum:* `ASTNode::clone()` implementasyonu bu fazın en kritik ve hata yapmaya açık kısmıdır, parent pointer bağlarına azami dikkat gösterilmelidir."""
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
for issue in issues_data:
|
||||||
|
endpoint = f"{REPO_PATH}/issues"
|
||||||
|
res = make_request(endpoint, method="POST", data=issue)
|
||||||
|
print(f"Created issue #{res['number']}: {res['title']}")
|
||||||
|
|
@ -0,0 +1,499 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Sozdizimi/davranis golden-test senaryolari + CLI/UX + kalite-mimari fikirleri.
|
||||||
|
|
||||||
|
"[Test]" basliklilar: somut .sqt kodu + beklenen cikti/davranis iceren
|
||||||
|
golden-test taslaklaridir. Derleyici (Faz 0-4 + IR/VM) tamamlandiginda bu
|
||||||
|
dosyalar `examples/tests/` altina yerlestirilip regresyon testi olarak
|
||||||
|
kullanilabilir; ayni zamanda "syntax X soyle calismali" spesifikasyonudur -
|
||||||
|
calismiyorsa ilgili faz/pass'te bugfix konusu acar.
|
||||||
|
|
||||||
|
"[Fikir]" basliklilar onceki script'lerle aynisekilde fikir/backlog kayitlaridir.
|
||||||
|
"""
|
||||||
|
from gitea import make_request, REPO_PATH
|
||||||
|
|
||||||
|
L_FIKIR = 66
|
||||||
|
L_TEST = 73
|
||||||
|
L_CLIUX = 74
|
||||||
|
L_KALITE = 75
|
||||||
|
|
||||||
|
issues_data = [
|
||||||
|
# ---------------- Golden-test senaryolari ----------------
|
||||||
|
{
|
||||||
|
"title": "[Test] Temel Aritmetik, Operatör Önceliği ve `print` Çıktısı",
|
||||||
|
"labels": [L_TEST],
|
||||||
|
"body": """### Giriş (Nedir, Neden Önemli?)
|
||||||
|
Bu issue, derleyicinin **en temel** davranışını sabitleyen bir golden-test taslağıdır: sayı literalleri, dört işlem, operatör önceliği (precedence) ve `print` builtin'i. "Fibonacci çalıştı" hedefinden önce bile bu örneğin doğru çalışması gerekir — Pratt parser'ın (zaten yapılı) önceliği doğru uyguladığının kanıtıdır.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Test Kodu (`examples/tests/aritmetik.sqt`)
|
||||||
|
```c
|
||||||
|
int main() {
|
||||||
|
print(1 + 2 * 3); // çarpma toplamadan önce
|
||||||
|
print((1 + 2) * 3); // parantez önceliği
|
||||||
|
print(10 - 4 - 3); // soldan-sağa birliktelik (left-assoc)
|
||||||
|
print(10 / 3); // tamsayı bölmesi (truncation)
|
||||||
|
print(10 % 3); // mod
|
||||||
|
print(2 + 3 * 4 - 6 / 2);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Beklenen Çıktı (`saqut run examples/tests/aritmetik.sqt`)
|
||||||
|
```
|
||||||
|
7
|
||||||
|
9
|
||||||
|
3
|
||||||
|
3
|
||||||
|
1
|
||||||
|
11
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Açık Sorular / Bağlı Fazlar
|
||||||
|
- Bu test, `saqut run` (IR + VM, ADR-015) tamamlanmadan **AST** seviyesinde de kısmen doğrulanabilir: `saqut ast --optimized` ile constant folding (Faz 4) çalıştığında her `print` argümanı doğrudan tek bir `Literal` düğümüne katlanmalı (örn. `1 + 2 * 3` → `Literal(7)`).
|
||||||
|
- `10 / 3` → `3` (tamsayı bölmesi) davranışı tip denetiminde (Faz 3) açıkça test edilmeli; `int / int = int` (kayıp olabilir ama bu saQut'ta hata değil, sadece kesme/truncation — ADR-010'daki "kayıpsız literal" kuralından farklı bir konu, karıştırılmamalı).
|
||||||
|
|
||||||
|
*İmza/Yorum:* Bu dosya, "derleyici bitti mi?" sorusuna verilecek **ilk** somut cevaplardan biri olmalı — `examples/fibonacci.sqt`'den önce, daha küçük bir adım."""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "[Test] Döngüler — `while` / `do-while` / `for` Davranış Farkları",
|
||||||
|
"labels": [L_TEST],
|
||||||
|
"body": """### Giriş (Nedir, Neden Önemli?)
|
||||||
|
Üç döngü yapısının (`while`, `do-while`, `for`) **birbirinden farklı** davrandığı noktalar — özellikle `do-while`'ın gövdeyi **en az bir kez** çalıştırması — derleyicide kolayca karıştırılabilecek köşe durumlardır (edge case). Bu issue, bu farkları somut kodla sabitler.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Test Kodu 1 — Üç döngü de aynı sayıları üretmeli (`examples/tests/donguler_temel.sqt`)
|
||||||
|
```c
|
||||||
|
int main() {
|
||||||
|
int i = 0;
|
||||||
|
while (i < 3) {
|
||||||
|
print(i);
|
||||||
|
i = i + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int j = 0;
|
||||||
|
do {
|
||||||
|
print(j);
|
||||||
|
j = j + 1;
|
||||||
|
} while (j < 3);
|
||||||
|
|
||||||
|
for (int k = 0; k < 3; k = k + 1) {
|
||||||
|
print(k);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Beklenen Çıktı
|
||||||
|
```
|
||||||
|
0
|
||||||
|
1
|
||||||
|
2
|
||||||
|
0
|
||||||
|
1
|
||||||
|
2
|
||||||
|
0
|
||||||
|
1
|
||||||
|
2
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Test Kodu 2 — `do-while` koşulu en baştan yanlışsa bile gövde bir kez çalışır (`examples/tests/do_while_en_az_bir.sqt`)
|
||||||
|
```c
|
||||||
|
int main() {
|
||||||
|
int x = 5;
|
||||||
|
do {
|
||||||
|
print(x);
|
||||||
|
x = x + 1;
|
||||||
|
} while (x < 0); // baştan yanlış!
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Beklenen Çıktı
|
||||||
|
```
|
||||||
|
5
|
||||||
|
```
|
||||||
|
(Eğer çıktı **boş** ise — yani `do-while`, `while` gibi davranıyorsa — bu bir **bug**'dır: `do-while`'ın AST düğümü/IR alçaltması `while` ile aynı koddan üretilmiş ve gövdeyi koşuldan **sonra** kontrol etmiyor olabilir.)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Açık Sorular / Bağlı Fazlar
|
||||||
|
- `for` döngüsünün üç parçası (`init; condition; step`) ayrı ayrı `AST` düğümlerinde mi tutuluyor, yoksa `while`'a "alçaltılıyor" (desugar) mu? İkinci durumda, `for (int k = 0; ...)` içindeki `k`'nın scope'u (sadece `for` bloğuna özel mi?) Faz 2'de doğru kurulmalı.
|
||||||
|
- `break`/`continue` (E004 ile ilişkili) bu üç döngü türünde de test edilmeli — ayrı bir test dosyası (`donguler_break_continue.sqt`) eklenebilir.
|
||||||
|
|
||||||
|
*İmza/Yorum:* `do-while`/`while` karışıklığı, "derleyici neredeyse bitti ama syntax X yanlış davranıyor" tarzı klasik bug'lardandır — bu test erken yazılırsa erken yakalanır."""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "[Test] Bit Düzeyi (Bitwise) İşlemler — `&`, `|`, `^`, `~`, `<<`, `>>`",
|
||||||
|
"labels": [L_TEST],
|
||||||
|
"body": """### Giriş (Nedir, Neden Önemli?)
|
||||||
|
Tokenizer'da `&`, `|`, `^`, `~`, `<<`, `>>` token'ları zaten tanımlı (`src/tokenizer/tokenizer.hpp`). Ama bu operatörlerin **Pratt parser'da doğru öncelik** ile ayrıştırıldığı ve **tip denetiminde** (Faz 3, sadece `int` üzerinde tanımlı) doğru ele alındığı henüz doğrulanmadı. Bu issue, "karmaşık bit işlemleri" için bir golden-test taslağıdır.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Test Kodu (`examples/tests/bit_islemleri.sqt`)
|
||||||
|
```c
|
||||||
|
int main() {
|
||||||
|
int a = 12; // 0b1100
|
||||||
|
int b = 10; // 0b1010
|
||||||
|
|
||||||
|
print(a & b); // bitwise AND
|
||||||
|
print(a | b); // bitwise OR
|
||||||
|
print(a ^ b); // bitwise XOR
|
||||||
|
print(~a); // bitwise NOT (iki's complement)
|
||||||
|
print(a << 2); // sola kaydır
|
||||||
|
print(a >> 2); // sağa kaydır
|
||||||
|
|
||||||
|
// Operatör önceliği: << ve >>, + ve - 'dan düşük öncelikli olmalı (C kuralı)
|
||||||
|
print(1 << 2 + 1); // (2+1)=3 -> 1 << 3 = 8, yoksa (1<<2)+1=5 olur
|
||||||
|
print(a & b | b); // & , | 'dan önce: (a&b) | b = 8 | 10 = 10
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Beklenen Çıktı
|
||||||
|
```
|
||||||
|
8
|
||||||
|
14
|
||||||
|
6
|
||||||
|
-13
|
||||||
|
48
|
||||||
|
3
|
||||||
|
8
|
||||||
|
10
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Açık Sorular / Bağlı Fazlar
|
||||||
|
- `~a` için `-13` beklentisi, `int`'in **iki's complement (ikiye tümleyen) imzalı 32-bit** temsiline dayanır — bu, `src/core/type.hpp`'de `int`'in tam temsilinin (bit genişliği) **şimdiden** dokümante edilmesini gerektirir (Faz 0'a not).
|
||||||
|
- `1 << 2 + 1` örneği **kasıtlı olarak kafa karıştırıcı** — C'de `<<`/`>>` aritmetik operatörlerden (`+`/`-`) daha düşük önceliklidir. saQut Pratt parser'ı bu sırayı C ile aynı mı tutacak, yoksa daha "şaşırtmasız" bir öncelik mi tanımlayacak? **Karar ne olursa olsun docs'a yazılmalı**, çünkü bu tip kararlar "sessiz" kalırsa en çok şikayet edilen bug kaynağı olur.
|
||||||
|
- Bu operatörler sadece `int` için tanımlı olmalı; `bool & bool` veya `float << 1` gibi kullanımlar `E003` üretmeli (Faz 3'e not).
|
||||||
|
|
||||||
|
*İmza/Yorum:* "Karmaşık byte işlemleri" testi olarak istenen tam da bu — bit-manipülasyonu doğru çalışmayan bir derleyici, gerçek programlar (hash, sıkıştırma, bayrak/flag kümeleri) için kullanılamaz hale gelir."""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "[Test] Builtin Fonksiyonların Kullanım Örnekleri (`print`, `len`, `toString`, `parseInt`, ...)",
|
||||||
|
"labels": [L_TEST],
|
||||||
|
"body": """### Giriş (Nedir, Neden Önemli?)
|
||||||
|
`print` dışındaki builtin'ler henüz yazılmadı (bkz. "[Fikir] Builtin Fonksiyon Kataloğu", #89), ama bu issue **hedefin son hâlini** somut kodla göstermek için var — yani "builtin kataloğu" tamamlandığında bu dosyanın **aynen bu çıktıyı** vermesi beklenir. Erken yazmanın faydası: builtin imzaları (parametre/dönüş tipleri) bu örnekten geriye doğru türetilebilir.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Test Kodu (`examples/tests/builtin_fonksiyonlar.sqt`)
|
||||||
|
```c
|
||||||
|
int main() {
|
||||||
|
// print: farklı tiplerde çalışmalı
|
||||||
|
print(42);
|
||||||
|
print(3.14);
|
||||||
|
print("merhaba");
|
||||||
|
print(true);
|
||||||
|
|
||||||
|
// toString: sayıdan string'e
|
||||||
|
print(toString(123)); // "123"
|
||||||
|
|
||||||
|
// parseInt / parseFloat: string'den sayıya
|
||||||
|
print(parseInt("45") + 5); // 50
|
||||||
|
print(parseFloat("2.5") * 2.0); // 5.0
|
||||||
|
|
||||||
|
// len: string ve array uzunluğu
|
||||||
|
print(len("saqut")); // 5
|
||||||
|
|
||||||
|
int[] sayilar = {10, 20, 30};
|
||||||
|
print(len(sayilar)); // 3
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Beklenen Çıktı
|
||||||
|
```
|
||||||
|
42
|
||||||
|
3.14
|
||||||
|
merhaba
|
||||||
|
true
|
||||||
|
123
|
||||||
|
50
|
||||||
|
5.0
|
||||||
|
5
|
||||||
|
3
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Açık Sorular / Bağlı Fazlar
|
||||||
|
- `print(true)` çıktısı `true`/`false` mi yoksa `1`/`0` mı olmalı? saQut'ta `bool` ayrı bir tip olduğu için (ADR-010, gizli int↔bool dönüşümü de yok varsayımıyla) `true`/`false` **daha tutarlı** görünüyor — ama bu kararın `print`'in builtin tablosuna (#89) not edilmesi gerekir.
|
||||||
|
- `int[] sayilar = {10, 20, 30};` — array literal sözdizimi (`{...}`) şu an dilde **tanımlı mı**? Eğer değilse, bu issue aynı zamanda "array literal sözdizimi eklenmeli" şeklinde bir alt-konu açar (Faz 1/parser'a not).
|
||||||
|
- `parseFloat("2.5") * 2.0` → `5.0` çıktısının `5` değil `5.0` (ondalık nokta korunarak) basılması, `print`'in `float` biçimlendirmesi için bir kural gerektirir — bu kural baştan netleşmeli (örn. her zaman en az bir ondalık basamak).
|
||||||
|
|
||||||
|
*İmza/Yorum:* Bu dosya, builtin kataloğu (#89) ve minimal stdlib (#90) issue'larının "kabul testi" (acceptance test) gibi düşünülebilir."""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "[Test] Struct + Array Birlikte Kullanım Senaryosu",
|
||||||
|
"labels": [L_TEST],
|
||||||
|
"body": """### Giriş (Nedir, Neden Önemli?)
|
||||||
|
`struct` ve `int[]` dil kimliğinde "var" olarak işaretli ama bu ikisinin **birlikte** kullanıldığı (struct içinde array, array of struct) bir örnek henüz yazılmadı. Bu kombinasyon, value-semantics (değer semantiği) kopyalama davranışını (bkz. #79 "Array ve Struct'ların Runtime Bellek Düzeni") en çok zorlayan senaryodur.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Test Kodu (`examples/tests/struct_array.sqt`)
|
||||||
|
```c
|
||||||
|
struct Ogrenci {
|
||||||
|
string ad;
|
||||||
|
int notlar[3];
|
||||||
|
}
|
||||||
|
|
||||||
|
int toplaNotlar(Ogrenci o) {
|
||||||
|
int toplam = 0;
|
||||||
|
for (int i = 0; i < 3; i = i + 1) {
|
||||||
|
toplam = toplam + o.notlar[i];
|
||||||
|
}
|
||||||
|
return toplam;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
Ogrenci ali;
|
||||||
|
ali.ad = "Ali";
|
||||||
|
ali.notlar[0] = 70;
|
||||||
|
ali.notlar[1] = 85;
|
||||||
|
ali.notlar[2] = 90;
|
||||||
|
|
||||||
|
print(ali.ad);
|
||||||
|
print(toplaNotlar(ali));
|
||||||
|
|
||||||
|
// value semantics: kopya degisse de orijinal degismemeli
|
||||||
|
Ogrenci kopya = ali;
|
||||||
|
kopya.notlar[0] = 0;
|
||||||
|
print(toplaNotlar(ali)); // değişmemeli: 245
|
||||||
|
print(toplaNotlar(kopya)); // değişmeli: 175
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Beklenen Çıktı
|
||||||
|
```
|
||||||
|
Ali
|
||||||
|
245
|
||||||
|
245
|
||||||
|
175
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Açık Sorular / Bağlı Fazlar
|
||||||
|
- `int notlar[3];` sözdizimi (struct alanı olarak sabit-boyutlu array) — parser bunu destekliyor mu, yoksa `int[3] notlar;` mı olmalı? Dil kimliğinde array sözdizimi `int[]` olarak yazılmış ama boyutlu hali (`[3]`) henüz örneklenmemiş — bu issue, sözdizimini netleştirme ihtiyacını da gösterir.
|
||||||
|
- `Ogrenci kopya = ali;` satırının **gerçekten derin kopya** yapması (struct içindeki array dahil) — bu, #79'daki "struct kopyalama IR'de alan-alan mı genişler" sorusunun **somut test vakasıdır**. Eğer `kopya.notlar[0] = 0;` satırı `ali.notlar[0]`'ı da değiştirirse (yüzeysel kopya/shallow copy), bu **value semantics kuralının ihlalidir** — kritik bir bug.
|
||||||
|
|
||||||
|
*İmza/Yorum:* Bu test, "value semantics" kilitli kararının (dil kimliği tablosu) en sıkı sınandığı yer — diğer tüm testler geçse bile bu test başarısızsa, dilin temel garantisi bozulmuş demektir."""
|
||||||
|
},
|
||||||
|
# ---------------- Optimizasyon test senaryolari ----------------
|
||||||
|
{
|
||||||
|
"title": "[Test] Optimizasyon — Constant Folding Doğrulama (`--optimized` Öncesi/Sonrası)",
|
||||||
|
"labels": [L_TEST],
|
||||||
|
"body": """### Giriş (Nedir, Neden Önemli?)
|
||||||
|
Faz 4'ün constant folding pass'i, sabit ifadeleri derleme zamanında hesaplayıp `Literal` düğümüyle değiştirmeli (bkz. roadmap Faz 4). Bu issue, "öncesi/sonrası" AST karşılaştırmasının **tam olarak nasıl görünmesi gerektiğini** somutlaştırır.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Test Kodu (`examples/tests/opt_constant_folding.sqt`)
|
||||||
|
```c
|
||||||
|
int main() {
|
||||||
|
int x = 2 + 3 * 4; // sabit ifade -> 14
|
||||||
|
int y = (10 - 4) / 2; // sabit ifade -> 3
|
||||||
|
int z = x + y; // x, y sabit olduğu için z de katlanabilir -> 17
|
||||||
|
int w = 10 / 0; // sıfıra bölme: KATLANMAZ, W002 uyarısı
|
||||||
|
print(z);
|
||||||
|
print(w);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Beklenen Davranış
|
||||||
|
- `saqut ast examples/tests/opt_constant_folding.sqt` (bayraksız): orijinal AST — `x`'in initializer'ı `BinaryExpression(2, +, BinaryExpression(3, *, 4))` olarak **değişmeden** görünür.
|
||||||
|
- `saqut ast examples/tests/opt_constant_folding.sqt --optimized`: klonlanmış AST'de:
|
||||||
|
- `x`'in initializer'ı → `Literal(14)`
|
||||||
|
- `y`'nin initializer'ı → `Literal(3)`
|
||||||
|
- `z`'nin initializer'ı → `Literal(17)` (**zincirleme**: `x` ve `y` önce katlanmalı, sonra `z = x + y` de katlanabilmeli — bu, fixpoint döngüsünün gerekliliğinin kanıtıdır)
|
||||||
|
- `w`'nin initializer'ı → **değişmez** (`10 / 0` kalır), ve diagnostic çıktısında `W002` (sıfıra bölme) uyarısı görünür.
|
||||||
|
- `saqut run examples/tests/opt_constant_folding.sqt` çıktısı (VM tamamlandığında): `17` ve ardından `10 / 0` çalışma-zamanı hatası (bu son satır, IR/VM tasarımına `E0xx`/runtime-error kategorisi notu olarak düşülmeli).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Açık Sorular / Bağlı Fazlar
|
||||||
|
- "Zincirleme katlama" (`z = x + y`, `x` ve `y` kendileri katlanmış sabitlerden geliyor) tek bir fixpoint turunda mı yoksa birden fazla turda mı gerçekleşmeli — bu, ADR-009'daki fixpoint/iterasyon-tavanı kararının **somut test sayısı** olabilir (örn. "bu örnek 2 turda kararlı hale gelmeli").
|
||||||
|
- `10 / 0` ifadesinin **derleme zamanında uyarı, çalışma zamanında hata** ayrımı IR/VM tasarımına (#74-78) açıkça not edilmeli.
|
||||||
|
|
||||||
|
*İmza/Yorum:* Bu test dosyası, Faz 4'ün "bitti" tanımının resmi kanıtı olabilir — roadmap'teki "Doğrulama" bölümüne doğrudan eklenebilir."""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "[Test] Optimizasyon — Dead Code Elimination Doğrulama (Ölü Kod Temizliği)",
|
||||||
|
"labels": [L_TEST],
|
||||||
|
"body": """### Giriş (Nedir, Neden Önemli?)
|
||||||
|
Faz 4'ün DCE (Dead Code Elimination, Ölü Kod Eleme) pass'i, erişilemez kodu ve kullanılmayan değişkenleri temizlemeli. Bu issue, "ölü kod" sayılan **farklı durumların** her birini ayrı ayrı örnekler — DCE'nin sadece "return sonrası kod" ile sınırlı kalmaması gerektiğini gösterir.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Test Kodu (`examples/tests/opt_dead_code.sqt`)
|
||||||
|
```c
|
||||||
|
int hesapla(int n) {
|
||||||
|
int kullanilmayan = 99; // hiç kullanılmıyor -> W001
|
||||||
|
|
||||||
|
if (n > 0) {
|
||||||
|
return n * 2;
|
||||||
|
print(n); // return sonrası -> erişilemez, W003
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
int olu = 5; // fonksiyonun erişilemez kuyruğu -> W003
|
||||||
|
return olu;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
print(hesapla(5));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Beklenen Davranış
|
||||||
|
- `saqut ast examples/tests/opt_dead_code.sqt --optimized` çıktısında:
|
||||||
|
- `int kullanilmayan = 99;` satırı **silinmiş** olmalı (referans sayısı 0 → Faz 2'nin `references` listesi boş).
|
||||||
|
- `print(n);` (return sonrası) **silinmiş** olmalı.
|
||||||
|
- `int olu = 5; return olu;` (fonksiyonun son `return -1;`'inden sonraki erişilemez kuyruk) **silinmiş** olmalı.
|
||||||
|
- Diagnostic çıktısında **3 uyarı** görünmeli: `W001` (kullanılmayan değişken), iki adet `W003` (erişilemez kod) — konum bilgileriyle (satır/sütun) birlikte.
|
||||||
|
- `saqut ast examples/tests/opt_dead_code.sqt` (bayraksız, optimize edilmemiş): **hiçbir satır silinmemiş**, ama uyarılar yine de raporlanabilir (uyarı raporlama optimizasyondan bağımsız olabilir — bu ayrım netleştirilmeli).
|
||||||
|
- `saqut run examples/tests/opt_dead_code.sqt` çıktısı (optimize edilsin/edilmesin aynı olmalı — **DCE anlamı değiştirmez**, sadece temizler): `10`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Açık Sorular / Bağlı Fazlar
|
||||||
|
- "Kullanılmayan değişken" (`W001`) tespiti Faz 2'nin `references` sayacına dayanır — ama bu sayaç **optimize edilmemiş orijinal AST'de** mi tutulur, yoksa her fixpoint turunda klon üzerinde yeniden mi hesaplanır (ADR-009, "flow-bound analiz her turda klon üzerinde yeniden hesaplanır")? Bu örnek, o kararın doğru uygulanıp uygulanmadığının testi.
|
||||||
|
- Fonksiyonun "erişilemez kuyruğu" (son `return`'den sonraki kod) ile "if içindeki return sonrası kod" aynı `isReachable=false` mekanizmasıyla mı işaretleniyor — yoksa farklı kod yolları mı?
|
||||||
|
|
||||||
|
*İmza/Yorum:* "deadcode elimination eklenecek" değil, tam olarak kullanıcının istediği gibi: "optimizasyonlu/optimizasyonsuz çıktı karşılaştırıldığında gereksiz kod gözlemlenene kadar" çalışılacak somut hedef budur."""
|
||||||
|
},
|
||||||
|
# ---------------- CLI / UX ----------------
|
||||||
|
{
|
||||||
|
"title": "[Fikir] CLI'da AST Görüntüleme — Ağaç (Tree), Tablo ve `--format=dot` Seçenekleri",
|
||||||
|
"labels": [L_FIKIR, L_CLIUX],
|
||||||
|
"body": """### Giriş (Nedir, Neden Önemli?)
|
||||||
|
Şu an `saqut ast` ham JSON basıyor. JSON, makineler için ideal ama bir insanın "bu kod nasıl bir ağaca dönüşmüş" sorusuna hızlı cevap vermesi için **okunması zor**. "Programlanabilir derleyici" felsefesi hem makine-okur (JSON) hem **insan-okur** çıktıyı hak ediyor.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Gelişme (Olası Yaklaşımlar)
|
||||||
|
- `saqut ast file --format=json` (mevcut, varsayılan kalabilir), `--format=tree`: terminalde girintili/kutu-çizgili bir ağaç (örn. `tree` komutunun çıktısına benzer: `├──`, `└──`).
|
||||||
|
- `--format=dot`: Graphviz DOT formatı — `saqut ast file --format=dot | dot -Tpng -o ast.png` ile görsel AST diyagramı üretilebilir (#43'te zaten "AST görselleştirme" fikri vardı, bu onun somut hâli).
|
||||||
|
- `--format=table`: her düğümü satır olarak basan, `tip | konum | değer` kolonlarına sahip düz bir tablo — `grep`/`awk` ile script'lenmesi kolay bir ara format.
|
||||||
|
- Renkli terminal çıktısı (`--color`): düğüm tipine göre (Expression mavi, Statement sarı, Literal yeşil gibi) ANSI renk kodları — `--optimized` ile birlikte kullanılırsa, **silinen düğümler kırmızı/üstü çizili** gösterilebilir (DCE'nin görsel kanıtı).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Açık Sorular
|
||||||
|
- `--format=tree`/`table`/`dot` çıktıları AST'nin **tam** bilgisini mi taşır (her alan), yoksa "özet" mi (sadece tip + konum + kısa değer)? Tam JSON her zaman `--format=json` ile erişilebilir kalmalı — diğerleri "insan için özet" olabilir.
|
||||||
|
- Bu format seçenekleri `saqut symbols` ve ileride `saqut ir` için de **aynı bayrak isimleriyle** tutarlı olmalı mı (örn. her komut `--format=tree/json/dot/table` desteklesin — CLI'da tek bir ortak altyapı, `src/cli/format.hpp` gibi)?
|
||||||
|
|
||||||
|
*İmza/Yorum:* `--format=dot` özellikle düşük efor / yüksek "vitrin" değeri taşıyor — bir blog yazısında/README'de "işte saQut'un AST'si" diye gösterilebilecek bir görsel üretir."""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "[Fikir] CLI Komut Seti Genişletme Önerileri (`check`, `explain`, `watch`)",
|
||||||
|
"labels": [L_FIKIR, L_CLIUX],
|
||||||
|
"body": """### Giriş (Nedir, Neden Önemli?)
|
||||||
|
Mevcut/planlanan komutlar (`tokens`, `ast`, `symbols`, `run`, ileride `ir`, `fmt`, `lsp`) hep "bir aşamanın çıktısını göster" mantığında. Bu issue, geliştirici deneyimini iyileştirecek **birleşik/yardımcı** komutları tartışır.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Gelişme (Olası Yaklaşımlar)
|
||||||
|
- **`saqut check file`**: sadece derleme hatalarını (Faz 0-3 diagnostic'leri) raporlar, **hiçbir çıktı üretmez/çalıştırmaz** — "bu kod derlenir mi?" sorusuna hızlı cevap. CI'larda (otomatik test sistemlerinde) kullanışlı. Çıkış kodu (exit code) `0` = hatasız, `1` = hata var.
|
||||||
|
- **`saqut explain E003`**: bir hata kodunun ne anlama geldiğini, **neden** var olduğunu (ilgili ADR'ye atıfla) ve örnek doğru/yanlış kod gösteren bir açıklama basar — hata kataloğunun (roadmap'teki tablo) CLI'dan erişilebilir hâli. "[Fikir] Akıllı Diagnostic" (#98) ile doğrudan ilişkili.
|
||||||
|
- **`saqut watch file --run`**: dosya her kaydedildiğinde otomatik olarak `check`/`run` çalıştırır, sonucu terminalde gösterir — hızlı geri-bildirim döngüsü (`cargo watch` benzeri). v0 için basit bir dosya-değişikliği izleme (polling veya `inotify`) yeterli.
|
||||||
|
- **`saqut --version` / `saqut --help`**: temel ama unutulmaması gereken iskelet komutlar — her CLI aracının beklediği standart.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Açık Sorular
|
||||||
|
- `saqut explain` kataloğu, hata mesajlarının içine gömülü mü olacak (örn. her hata "detaylar için: `saqut explain E003`" diye bitiyor), yoksa ayrı statik bir JSON/Markdown kataloğundan mı okunacak?
|
||||||
|
- `saqut watch`, LSP (#91) ile fonksiyonel olarak örtüşüyor — ikisi de "kodu değiştirdikçe geri bildirim" sağlıyor. `watch` LSP'den **önce**, basit/terminal-tabanlı bir "ara çözüm" olarak mı konumlanmalı?
|
||||||
|
|
||||||
|
*İmza/Yorum:* `saqut check` ve `saqut explain`, düşük maliyetli ama günlük kullanımda en çok hissedilecek iyileştirmeler — Faz 0'ın `DiagnosticEngine`'i bittiği anda `check` neredeyse bedavaya gelir."""
|
||||||
|
},
|
||||||
|
# ---------------- Kalite / Mimari tavsiyeler ----------------
|
||||||
|
{
|
||||||
|
"title": "[Fikir] Performans için C Tarzı Tasarım Kararları (Hız Önceliksiz Ama Bilinçli)",
|
||||||
|
"labels": [L_FIKIR, L_KALITE],
|
||||||
|
"body": """### Giriş (Nedir, Neden Önemli?)
|
||||||
|
ADR-015 "öncelik determinizm + incelenebilirlik, ham hız değil" diyor — bu **doğru** bir karar. Ama "hız öncelik değil" ile "gereksiz yere yavaş" aynı şey değildir. Bu issue, **mimariyi karmaşıklaştırmadan** (yeni soyutlama eklemeden) elde edilebilecek "bedava" performans kazanımlarını listeler — C'nin "ne kadar az iş, o kadar hızlı" felsefesinden ilham alarak.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Gelişme (Tavsiyeler)
|
||||||
|
- **Gereksiz kopyalama'dan kaçın:** AST düğümleri ve `Type`/`Symbol` nesneleri `std::string` gibi alanlar taşıyor — bunlar fonksiyonlara **referans/`const&`** ile geçilmeli, değer ile değil. Bu, "header-only" (ADR-003) ile çatışmaz, sadece dikkatli imza yazımı gerektirir.
|
||||||
|
- **`std::unique_ptr` ile sahiplik (ownership) net olsun** (#44'te bahsedilen issue tipi) — bellek sahipliği belirsizse hem bug hem performans kaybı (gereksiz `shared_ptr`/kopya) olur.
|
||||||
|
- **Tokenizer/Lexer tek geçişte (single-pass) çalışıyor mu** — kaynak dosya birden fazla kez taranıyorsa (örn. önce tokenize, sonra tekrar karakter-karakter konum hesaplama), bu birleştirilebilir.
|
||||||
|
- **IR/VM tasarımında (ileride):** yığın-tabanlı VM'de (#76) gereksiz push/pop zincirleri, basit "peephole" (göz ucu) optimizasyonlarla (örn. `push X; pop` → hiçbir şey) IR seviyesinde temizlenebilir — bu, Faz 4'ün constant folding'ine **benzer** ama IR'e özel, küçük bir ek pass olabilir.
|
||||||
|
- **Derleme zamanı (`saqut` binary'sinin kendi derlenme hızı):** header-only + agresif template kullanımı derleme süresini şişirebilir — `-Wall -Wextra` yanında zaman zaman derleme süresi de (örn. `time cmake --build`) izlenmeli.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Açık Sorular
|
||||||
|
- "Bedava" optimizasyonlar (kopya azaltma, peephole) ile "erken optimizasyon yapma" ilkesi (roadmap'teki "önce dikey dilim") arasındaki çizgi nerede? Önerim: **mimariyi değiştirmeyen, sadece imza/kopya düzeltmeleri** her zaman yapılabilir; **yeni pass/altyapı gerektirenler** (peephole gibi) Faz 4 sonrasına bırakılır.
|
||||||
|
|
||||||
|
*İmza/Yorum:* "C gibi hızlı olmak", saQut için "C kadar hızlı VM" anlamına gelmek zorunda değil — "gereksiz iş yapmayan, dikkatli yazılmış C++ kodu" anlamına gelebilir; bu, ADR-015 ile çatışmaz."""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "[Fikir] Güvenlik ve Stabilite için Java Tarzı Tasarım Kararları (Sınır Kontrolü, Hata İzolasyonu)",
|
||||||
|
"labels": [L_FIKIR, L_KALITE],
|
||||||
|
"body": """### Giriş (Nedir, Neden Önemli?)
|
||||||
|
saQut'ta kullanıcıya açık pointer yok (dil kimliği) — bu, C'nin en büyük güvenlik açığı kaynağı olan "pointer aritmetiği"ni baştan eliyor. Ama Java'nın asıl gücü bundan da fazlası: **her hata kontrollü bir şekilde yakalanır, programın geri kalanı çökmeden devam edebilir** (exception/hata izolasyonu) ve **dizi sınırları her zaman kontrol edilir**. Bu issue, bu ilkelerin saQut'a nasıl yansıyacağını tartışır.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Gelişme (Tavsiyeler)
|
||||||
|
- **Array sınır kontrolü (bounds checking):** `int[3] notlar; notlar[5] = 1;` gibi bir erişim — eğer boyut derleme zamanında biliniyorsa (`E009` zaten "array boyutu sabit değil" hatasını kapsıyor, ama "sabit boyut + sınır-dışı indeks" ayrı bir konu) **derleme zamanında** `E0xx` ile yakalanmalı; eğer indeks çalışma zamanı değeriyse (`notlar[i]`, `i` döngü değişkeni), **VM çalışma zamanında kontrol etmeli** ve kontrollü bir "runtime error" üretmeli (C'deki sessiz bellek bozulmasının tam tersi).
|
||||||
|
- **Çalışma zamanı hatalarının kategorize edilmesi:** sıfıra bölme (Faz 4'te `W002` derleme-zamanı uyarısı var, ama çalışma zamanında gerçek bir bölme `10 / x` ile `x=0` olursa ne olur?), dizi sınır taşması, derinlik taşması (call stack overflow, #78) — bunların hepsi **aynı "RuntimeError" ailesinde**, konum bilgisiyle (hangi `print`/satırda patladı) raporlanmalı; VM **segfault** ile çökmemeli.
|
||||||
|
- **"Programın bir kısmı patlasın, derleyici/VM çökmesin":** VM'in kendi C++ kodunda `assert`/exception kullanımı, kullanıcı programındaki bir hatayı (örn. sınır-dışı erişim) **VM crash'ine** dönüştürmemeli — bu, VM geliştirilirken her runtime-error noktasında "bu durum kullanıcıya nasıl raporlanır" sorusunun sorulması demektir.
|
||||||
|
- **Tip sisteminin sıkılığı (ADR-010) zaten "Java tarzı" bir güvenlik katmanı** — gizli dönüşüm yok kuralı, C'deki "implicit int→pointer" gibi klasik hata sınıflarını zaten önlüyor. Bu issue bunu **devam ettirme** çağrısıdır, yeni bir şey eklemiyor.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Açık Sorular
|
||||||
|
- Çalışma zamanı hataları (array sınır taşması vb.) için **yeni bir diagnostic kategorisi** (`R001`, `R002`... "Runtime" öneki ile, `E`/`W`'den ayrı) mı açılmalı — IR/VM tasarım issue'larına (#74-78) not edilmeli.
|
||||||
|
- Sınır kontrolü **her zaman** mı açık olacak, yoksa "release modu"nda (varsa böyle bir kavram) kapatılabilir mi — ADR-015'in "determinizm > hız" ilkesi düşünülürse, **her zaman açık** olması daha tutarlı görünüyor.
|
||||||
|
|
||||||
|
*İmza/Yorum:* "Güvenli dil" derken kastedilen genelde "bellek güvenliği" — saQut bunu zaten pointer'sızlıkla büyük ölçüde kazandı; kalan iş, **çalışma zamanı hatalarını zarif bir şekilde raporlamak**."""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "[Fikir] Modernlik için Go Tarzı Tasarım Kararları (Basitlik, Hızlı Geri-Bildirim, Tek Binary)",
|
||||||
|
"labels": [L_FIKIR, L_KALITE],
|
||||||
|
"body": """### Giriş (Nedir, Neden Önemli?)
|
||||||
|
Go'nun başarısının çoğu **dil özelliklerinden değil, geliştirici deneyiminden** geliyor: çok hızlı derleme, yerleşik formatter (`gofmt`), tek-binary dağıtım, anlaşılır hata mesajları, "az ama öz" bir standart kütüphane. saQut zaten küçük/basit bir dil — bu issue, Go'nun **araç zinciri (toolchain) felsefesini** saQut'a nasıl taşıyabileceğimizi listeler.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Gelişme (Tavsiyeler)
|
||||||
|
- **Tek binary, sıfır bağımlılık:** `saqut` CLI'sı tüm komutları (`tokens`, `ast`, `symbols`, ileride `run`, `fmt`, `lsp`) **tek bir çalıştırılabilir dosyada** barındırır — kullanıcı hiçbir ek paket/runtime kurmaz. Header-only C++ (ADR-003) bu hedefe zaten doğal olarak uygun; bu issue sadece **bunu bir hedef olarak yazıya dökmek**.
|
||||||
|
- **Hızlı derleme = hızlı geri-bildirim:** Go'nun derleme hızı, "kodu yaz → hemen çalıştır" döngüsünü mümkün kılıyor. saQut için bu, `saqut run` (IR+VM) ile birleşince **"saQut programını derleyip çalıştırmak, C++ programını derlemekten çok daha hızlı olmalı"** gibi somut bir hedef olabilir — VM yorumlayıcı olduğu için bu zaten doğal bir avantaj.
|
||||||
|
- **`gofmt` zaten bir issue olarak var (#93, `saqut fmt`)** — Go'nun "tartışmasız tek doğru format" felsefesi: format ayarlanabilir **olmamalı** (yapılandırma dosyası yok), bu da formatter'ın tasarımını basitleştirir.
|
||||||
|
- **Anlaşılır hata mesajları (#98 ile ilişkili):** Go'nun hata mesajları kısa ve doğrudandır (`undefined: x`). saQut'un hata kataloğu (E001 vb.) zaten bu yönde — bu issue, mesaj **dilinin** (Türkçe/İngilizce) ve **tonunun** (kısa cümle + örnek) standartlaştırılmasını önerir.
|
||||||
|
- **Yerleşik test çalıştırıcı (#96 ile ilişkili)** ve **modül sistemi (#81-83)** — Go'nun `go test` ve `go mod`'u, dilin "etrafına" değil **içine** gömülü araçlar. saQut'un CLI'sı da bu felsefeyle büyümeli: her yeni özellik (test, format, modül) **ayrı bir 3. parti araç değil, `saqut` alt-komutu** olmalı.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Açık Sorular
|
||||||
|
- "Tek binary" hedefi, FFI (#88) ile gerilim yaratabilir mi — kullanıcı kendi C++ host fonksiyonlarını eklemek isterse, bu "tek binary"yi nasıl etkiler (dinamik kütüphane yükleme mi, yeniden derleme mi)?
|
||||||
|
- Hata mesajı dili: şu anki dokümantasyon Türkçe — CLI çıktısı da Türkçe mi olacak, yoksa İngilizce + Türkçe `explain` (#issue) mi? Bu, uluslararası kullanıcı kitlesi hedefleniyorsa önemli bir erken karar.
|
||||||
|
|
||||||
|
*İmza/Yorum:* saQut'un "alet çantası" kimliği ile Go'nun "az şey, ama hepsi birlikte ve iyi çalışıyor" felsefesi doğal bir eşleşme — bu issue, var olan diğer issue'ları (#91-98) tek bir "araç zinciri vizyonu" şemsiyesi altında toplama denemesidir."""
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
for issue in issues_data:
|
||||||
|
endpoint = f"{REPO_PATH}/issues"
|
||||||
|
res = make_request(endpoint, method="POST", data=issue)
|
||||||
|
print(f"Created issue #{res['number']}: {res['title']}")
|
||||||
|
|
@ -0,0 +1,153 @@
|
||||||
|
"""
|
||||||
|
saQut büyük kaynak dosyası üretici.
|
||||||
|
Sözdizimsel ve semantik olarak geçerli, çeşitli yapılar içeren
|
||||||
|
gerçekçi bir .sqt dosyası üretir.
|
||||||
|
|
||||||
|
Hedef: ~1 MB
|
||||||
|
"""
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import random
|
||||||
|
|
||||||
|
random.seed(42)
|
||||||
|
|
||||||
|
lines = []
|
||||||
|
|
||||||
|
def emit(s=""):
|
||||||
|
lines.append(s)
|
||||||
|
|
||||||
|
# ── Başlık ────────────────────────────────────────────────────────────────────
|
||||||
|
emit("// saQut büyük kaynak dosyası — otomatik üretildi (gen_large.py)")
|
||||||
|
emit("// Amaç: ast / symbols / check komutları için performans ölçümü")
|
||||||
|
emit()
|
||||||
|
|
||||||
|
# ── Struct tanımları ──────────────────────────────────────────────────────────
|
||||||
|
STRUCT_COUNT = 20
|
||||||
|
for i in range(STRUCT_COUNT):
|
||||||
|
emit(f"struct Vec{i} {{")
|
||||||
|
emit(f" int x;")
|
||||||
|
emit(f" int y;")
|
||||||
|
emit(f" int z;")
|
||||||
|
emit(f" float w;")
|
||||||
|
emit(f"}}")
|
||||||
|
emit()
|
||||||
|
|
||||||
|
# ── Global değişkenler ────────────────────────────────────────────────────────
|
||||||
|
for i in range(50):
|
||||||
|
emit(f"int g_{i} = {i * 3 + 1};")
|
||||||
|
emit()
|
||||||
|
|
||||||
|
# ── Yardımcı: rastgele aritmetik ifade (derinlik sınırlı) ─────────────────────
|
||||||
|
def expr(depth=0, vars=None):
|
||||||
|
if vars is None:
|
||||||
|
vars = ["a", "b", "c", "x", "n"]
|
||||||
|
if depth >= 3 or random.random() < 0.35:
|
||||||
|
choice = random.random()
|
||||||
|
if choice < 0.5 and vars:
|
||||||
|
return random.choice(vars)
|
||||||
|
elif choice < 0.8:
|
||||||
|
return str(random.randint(1, 100))
|
||||||
|
else:
|
||||||
|
return str(random.randint(1, 20))
|
||||||
|
ops = ["+", "-", "*"]
|
||||||
|
op = random.choice(ops)
|
||||||
|
return f"({expr(depth+1, vars)} {op} {expr(depth+1, vars)})"
|
||||||
|
|
||||||
|
def cond(vars=None):
|
||||||
|
if vars is None:
|
||||||
|
vars = ["a", "b", "n"]
|
||||||
|
ops = ["<", ">", "<=", ">=", "==", "!="]
|
||||||
|
op = random.choice(ops)
|
||||||
|
lhs = expr(0, vars)
|
||||||
|
rhs = expr(0, vars)
|
||||||
|
return f"{lhs} {op} {rhs}"
|
||||||
|
|
||||||
|
# ── Fonksiyon gövdesi üreteci ─────────────────────────────────────────────────
|
||||||
|
def gen_body(fn_idx, local_vars, indent=1):
|
||||||
|
pad = " " * indent
|
||||||
|
code = []
|
||||||
|
|
||||||
|
# 2-5 lokal değişken tanımı — sadece declare edilenleri ifadelerde kullan
|
||||||
|
n_vars = random.randint(2, 5)
|
||||||
|
declared = []
|
||||||
|
for v in local_vars[:n_vars]:
|
||||||
|
# İlk değişken sadece sabitle başlatılır (henüz başka değişken yok)
|
||||||
|
init = expr(0, declared if declared else ["1"])
|
||||||
|
code.append(f"{pad}int {v} = {init};")
|
||||||
|
declared.append(v)
|
||||||
|
local_vars = declared # bundan sonra sadece declare edilenleri kullan
|
||||||
|
|
||||||
|
# 1-3 if bloğu
|
||||||
|
for _ in range(random.randint(1, 3)):
|
||||||
|
c = cond(local_vars)
|
||||||
|
then_val = expr(0, local_vars)
|
||||||
|
code.append(f"{pad}if ({c}) {{")
|
||||||
|
code.append(f"{pad} {local_vars[0]} = {then_val};")
|
||||||
|
if random.random() < 0.4:
|
||||||
|
else_val = expr(0, local_vars)
|
||||||
|
code.append(f"{pad}}} else {{")
|
||||||
|
code.append(f"{pad} {local_vars[0]} = {else_val};")
|
||||||
|
code.append(f"{pad}}}")
|
||||||
|
|
||||||
|
# 0-2 for döngüsü
|
||||||
|
for _ in range(random.randint(0, 2)):
|
||||||
|
bound = random.randint(3, 15)
|
||||||
|
inner = expr(0, local_vars)
|
||||||
|
code.append(f"{pad}for (int i = 0; i < {bound}; i = i + 1) {{")
|
||||||
|
code.append(f"{pad} {local_vars[0]} = {local_vars[0]} + {inner};")
|
||||||
|
code.append(f"{pad}}}")
|
||||||
|
|
||||||
|
# return
|
||||||
|
ret_val = expr(0, local_vars)
|
||||||
|
code.append(f"{pad}return {ret_val};")
|
||||||
|
return code
|
||||||
|
|
||||||
|
# ── Fonksiyon tanımları ───────────────────────────────────────────────────────
|
||||||
|
# Toplam ~5000 fonksiyon → ~1 MB hedef
|
||||||
|
FUNC_COUNT = 5000
|
||||||
|
func_names = [f"fn_{i:04d}" for i in range(FUNC_COUNT)]
|
||||||
|
func_params = {} # fname → n_params (çağrı sırasında eşleştirmek için)
|
||||||
|
|
||||||
|
for i, fname in enumerate(func_names):
|
||||||
|
# Parametre listesi (0-3 parametre)
|
||||||
|
n_params = random.randint(0, 3)
|
||||||
|
func_params[fname] = n_params
|
||||||
|
params = [f"int p{j}" for j in range(n_params)]
|
||||||
|
param_str = ", ".join(params)
|
||||||
|
|
||||||
|
# Lokal değişken adları
|
||||||
|
local_vars = ["a", "b", "c", "d", "e",
|
||||||
|
f"v{i % 10}", f"w{i % 7}"]
|
||||||
|
|
||||||
|
emit(f"int {fname}({param_str}) {{")
|
||||||
|
for line in gen_body(i, local_vars):
|
||||||
|
emit(line)
|
||||||
|
emit("}")
|
||||||
|
emit()
|
||||||
|
|
||||||
|
# ── Main ──────────────────────────────────────────────────────────────────────
|
||||||
|
emit("int main() {")
|
||||||
|
emit(" int result = 0;")
|
||||||
|
emit()
|
||||||
|
|
||||||
|
# Her 50 fonksiyonu bir kere çağır — argüman sayısı imzayla eşleştirildi
|
||||||
|
for i in range(0, FUNC_COUNT, 50):
|
||||||
|
fname = func_names[i]
|
||||||
|
n_params = func_params[fname]
|
||||||
|
args = ", ".join(str(random.randint(1, 20)) for _ in range(n_params))
|
||||||
|
emit(f" result = result + {fname}({args});")
|
||||||
|
|
||||||
|
emit()
|
||||||
|
emit(" print(result);")
|
||||||
|
emit(" return 0;")
|
||||||
|
emit("}")
|
||||||
|
|
||||||
|
# ── Yaz ───────────────────────────────────────────────────────────────────────
|
||||||
|
output = "\n".join(lines) + "\n"
|
||||||
|
path = sys.argv[1] if len(sys.argv) > 1 else "examples/large.sqt"
|
||||||
|
|
||||||
|
with open(path, "w", encoding="utf-8") as f:
|
||||||
|
f.write(output)
|
||||||
|
|
||||||
|
size_kb = len(output.encode("utf-8")) / 1024
|
||||||
|
print(f"Üretildi: {path} ({size_kb:.1f} KB, {len(lines)} satır)")
|
||||||
10
source.sqt
10
source.sqt
|
|
@ -1,10 +0,0 @@
|
||||||
int main() {
|
|
||||||
int x = 0;
|
|
||||||
while (x < 5) {
|
|
||||||
x = x + 1;
|
|
||||||
}
|
|
||||||
do {
|
|
||||||
x = x - 1;
|
|
||||||
} while (x > 0);
|
|
||||||
return x;
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1,147 @@
|
||||||
|
// ============================================================================
|
||||||
|
// saQut Compiler — CLI Argüman Ayrıştırıcı ve Kaynak Okuma
|
||||||
|
// ============================================================================
|
||||||
|
//
|
||||||
|
// DİZİN: src/cli/args.hpp
|
||||||
|
// BAĞIMLI: Yok (sadece standart kütüphane)
|
||||||
|
//
|
||||||
|
// AMAÇ:
|
||||||
|
// 1. POSIX tarzı argüman ayrıştırma
|
||||||
|
// 2. Kaynak dosya okuma (tüm komutlar tarafından paylaşılır)
|
||||||
|
//
|
||||||
|
// DESTEKLENEN FORMATLAR:
|
||||||
|
// saqut <komut> [dosya] [-o çıktı] [--format json] [--help]
|
||||||
|
// saqut run file:source.sqt (eski sözdizimi)
|
||||||
|
// saqut - (stdin — TODO)
|
||||||
|
//
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
#ifndef SAQUT_CLI_ARGS
|
||||||
|
#define SAQUT_CLI_ARGS
|
||||||
|
|
||||||
|
#include <fstream>
|
||||||
|
#include <iostream>
|
||||||
|
#include <sstream>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
struct CliArgs {
|
||||||
|
std::string command;
|
||||||
|
std::vector<std::string> positional;
|
||||||
|
std::string outputFile;
|
||||||
|
std::string format;
|
||||||
|
bool showHelp = false;
|
||||||
|
bool stdinMode = false;
|
||||||
|
bool compact = false; // --compact: boşluksuz JSON
|
||||||
|
bool optimized = false; // --optimized: sabit katlama + ölü kod eleme
|
||||||
|
};
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// parseArgs
|
||||||
|
// ============================================================================
|
||||||
|
inline CliArgs parseArgs(int argc, char* argv[]) {
|
||||||
|
CliArgs args;
|
||||||
|
|
||||||
|
for (int i = 1; i < argc; i++) {
|
||||||
|
std::string arg = argv[i];
|
||||||
|
|
||||||
|
if (arg == "-") {
|
||||||
|
args.stdinMode = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (arg == "-h" || arg == "--help") {
|
||||||
|
args.showHelp = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (arg.compare(0, 9, "--output=") == 0) {
|
||||||
|
args.outputFile = arg.substr(9);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (arg.compare(0, 9, "--format=") == 0) {
|
||||||
|
args.format = arg.substr(9);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (arg == "--output" || arg == "-o") {
|
||||||
|
if (i + 1 < argc) args.outputFile = argv[++i];
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (arg == "--format") {
|
||||||
|
if (i + 1 < argc) args.format = argv[++i];
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (arg == "--compact") {
|
||||||
|
args.compact = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (arg == "--optimized") {
|
||||||
|
args.optimized = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (arg.compare(0, 5, "file:") == 0) {
|
||||||
|
args.positional.push_back(arg.substr(5));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (arg.compare(0, 7, "output:") == 0) {
|
||||||
|
args.outputFile = arg.substr(7);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (arg.compare(0, 4, "ast:") == 0) {
|
||||||
|
args.outputFile = arg.substr(4);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// İlk argüman komut mu?
|
||||||
|
if (args.command.empty() && i == 1) {
|
||||||
|
if (arg == "run" || arg == "tokens" || arg == "ast" ||
|
||||||
|
arg == "symbols" || arg == "check" || arg == "ir" ||
|
||||||
|
arg == "compile" || arg == "parse" || arg == "transpile" ||
|
||||||
|
arg == "interpret") {
|
||||||
|
args.command = arg;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
args.command = "run";
|
||||||
|
args.positional.push_back(arg);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
args.positional.push_back(arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (args.command.empty()) args.command = "run";
|
||||||
|
if (args.positional.empty() && !args.stdinMode)
|
||||||
|
args.positional.push_back("source.sqt");
|
||||||
|
|
||||||
|
return args;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// readSource: Dosyadan veya stdin'den kaynak kod oku (TODO: stdin)
|
||||||
|
// ============================================================================
|
||||||
|
inline std::string readSource(const CliArgs& args) {
|
||||||
|
if (args.stdinMode) {
|
||||||
|
// TODO: std::cin'den EOF'a kadar oku
|
||||||
|
std::cerr << "TODO: stdin modu henüz desteklenmiyor\n";
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
if (args.positional.empty()) return "";
|
||||||
|
|
||||||
|
std::string path = args.positional[0];
|
||||||
|
std::ifstream file(path, std::ios::in | std::ios::binary);
|
||||||
|
if (!file.is_open()) {
|
||||||
|
std::cerr << "Hata: '" << path << "' dosyası açılamadı\n";
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
std::stringstream buffer;
|
||||||
|
buffer << file.rdbuf();
|
||||||
|
return buffer.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// inputFilePath: Kaynak dosyanın yolunu döndür (stdin modunda boş string)
|
||||||
|
// ============================================================================
|
||||||
|
inline std::string inputFilePath(const CliArgs& args) {
|
||||||
|
if (args.stdinMode || args.positional.empty()) return "";
|
||||||
|
return args.positional[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // SAQUT_CLI_ARGS
|
||||||
|
|
@ -0,0 +1,103 @@
|
||||||
|
// ============================================================================
|
||||||
|
// saQut Compiler — CLI Dispatcher
|
||||||
|
// ============================================================================
|
||||||
|
//
|
||||||
|
// DİZİN: src/cli/cli.hpp
|
||||||
|
// BAĞIMLI: args.hpp, commands/*
|
||||||
|
//
|
||||||
|
// AMAÇ:
|
||||||
|
// Komut kaydı ve dağıtımı. Yeni bir komut eklemek için:
|
||||||
|
// 1. src/cli/commands/x.hpp oluştur
|
||||||
|
// 2. registerCommand() ile kaydet
|
||||||
|
//
|
||||||
|
// MİMARİ:
|
||||||
|
// Her komut bir CliCommand struct'ıdır:
|
||||||
|
// - name: "run", "tokens", "ast", ...
|
||||||
|
// - description: Yardım metninde görünür
|
||||||
|
// - hidden: true ise yardımda listelenmez (alias'lar için)
|
||||||
|
// - execute: int(CliArgs&) döndürür (0 = başarılı)
|
||||||
|
//
|
||||||
|
// Komutlar lazy olarak include edilmez — her biri kendi header'ında
|
||||||
|
// inline fonksiyon olarak tanımlanır ve cli.hpp tarafından include edilir.
|
||||||
|
//
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
#ifndef SAQUT_CLI
|
||||||
|
#define SAQUT_CLI
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
#include <iostream>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include "cli/args.hpp"
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// CliCommand — Kayıtlı bir komut
|
||||||
|
// ============================================================================
|
||||||
|
struct CliCommand {
|
||||||
|
std::string name;
|
||||||
|
std::string description;
|
||||||
|
bool hidden = false; // true = yardımda gösterme
|
||||||
|
std::function<int(const CliArgs&)> execute;
|
||||||
|
};
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// CliDispatcher — Komut kaydı ve çalıştırma
|
||||||
|
// ============================================================================
|
||||||
|
class CliDispatcher {
|
||||||
|
public:
|
||||||
|
void registerCommand(const CliCommand& cmd) {
|
||||||
|
commands.push_back(cmd);
|
||||||
|
}
|
||||||
|
|
||||||
|
int dispatch(const CliArgs& args) const {
|
||||||
|
// Yardım özel durumu
|
||||||
|
if (args.showHelp || args.command == "help") {
|
||||||
|
printHelp();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Komutu bul
|
||||||
|
for (auto& cmd : commands) {
|
||||||
|
if (cmd.name == args.command) {
|
||||||
|
return cmd.execute(args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bilinmeyen komut
|
||||||
|
std::cerr << "Hata: Bilinmeyen komut '" << args.command << "'\n";
|
||||||
|
std::cerr << "Kullanılabilir komutlar için: saqut --help\n";
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void printHelp() const {
|
||||||
|
std::cout << "saQut Compiler — Dil Bağımsız Derleyici Alet Çantası\n\n";
|
||||||
|
std::cout << "KULLANIM:\n";
|
||||||
|
std::cout << " saqut <komut> [dosya] [seçenekler]\n";
|
||||||
|
std::cout << " saqut - (stdin modu — TODO)\n\n";
|
||||||
|
std::cout << "KOMUTLAR:\n";
|
||||||
|
|
||||||
|
for (auto& cmd : commands) {
|
||||||
|
if (cmd.hidden) continue;
|
||||||
|
std::cout << " " << cmd.name;
|
||||||
|
// 12 karaktere hizala
|
||||||
|
size_t pad = cmd.name.size() < 11 ? 11 - cmd.name.size() : 1;
|
||||||
|
std::cout << std::string(pad, ' ') << cmd.description << "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << "\nSEÇENEKLER:\n";
|
||||||
|
std::cout << " -o, --output <dosya> Çıktı dosyası\n";
|
||||||
|
std::cout << " --format <json|text> Çıktı formatı (varsayılan: text)\n";
|
||||||
|
std::cout << " -h, --help Bu yardım metni\n\n";
|
||||||
|
std::cout << "ÖRNEK:\n";
|
||||||
|
std::cout << " saqut run source.sqt\n";
|
||||||
|
std::cout << " saqut tokens source.sqt\n";
|
||||||
|
std::cout << " saqut ast source.sqt --format=json\n";
|
||||||
|
std::cout << " saqut symbols source.sqt\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::vector<CliCommand> commands;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // SAQUT_CLI
|
||||||
|
|
@ -0,0 +1,84 @@
|
||||||
|
// ============================================================================
|
||||||
|
// saQut CLI — ast komutu (JSON formatında AST hiyerarşisi + analiz)
|
||||||
|
//
|
||||||
|
// --optimized: sabit katlama + ölü kod eleme uygulandıktan sonra AST göster.
|
||||||
|
// Orijinal AST dokunulmaz; optimize edilmiş klon gösterilir.
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
#ifndef SAQUT_CLI_AST
|
||||||
|
#define SAQUT_CLI_AST
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <fstream>
|
||||||
|
#include "cli/args.hpp"
|
||||||
|
#include "tokenizer/tokenizer.hpp"
|
||||||
|
#include "parser/parser.hpp"
|
||||||
|
#include "symbol/symbol_table.hpp"
|
||||||
|
#include "symbol/symbol_collector.hpp"
|
||||||
|
#include "semantic/type_checker.hpp"
|
||||||
|
#include "semantic/structural_validator.hpp"
|
||||||
|
#include "diagnostic/diagnostic_engine.hpp"
|
||||||
|
#include "core/config.hpp"
|
||||||
|
#include "opt/optimization_manager.hpp"
|
||||||
|
#include "json.hpp"
|
||||||
|
|
||||||
|
inline int cmdAst(const CliArgs& args) {
|
||||||
|
std::string source = readSource(args);
|
||||||
|
if (source.empty()) return 1;
|
||||||
|
|
||||||
|
Tokenizer tokenizer;
|
||||||
|
auto tokens = tokenizer.scan(source, inputFilePath(args));
|
||||||
|
|
||||||
|
Parser parser;
|
||||||
|
ASTNode* ast = parser.parse(tokens);
|
||||||
|
|
||||||
|
if (!ast) {
|
||||||
|
std::cerr << "Hata: AST üretilemedi\n";
|
||||||
|
for (auto* t : tokens) delete t;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Sembol + tip analizi (--optimized için gerekli; yalın ast'te opsiyonel) ──
|
||||||
|
SymbolTable symbolTable;
|
||||||
|
DiagnosticEngine diag;
|
||||||
|
SymbolCollector(symbolTable, diag).collect(ast);
|
||||||
|
TypeChecker(symbolTable, diag).check(ast);
|
||||||
|
StructuralValidator(diag).validate(ast);
|
||||||
|
|
||||||
|
ASTNode* displayAst = ast; // gösterilecek ağaç (orijinal veya klon)
|
||||||
|
ASTNode* clonedAst = nullptr;
|
||||||
|
|
||||||
|
if (args.optimized) {
|
||||||
|
CompilerConfig cfg;
|
||||||
|
OptimizationManager mgr(cfg, diag);
|
||||||
|
clonedAst = mgr.optimize(ast, &symbolTable);
|
||||||
|
displayAst = clonedAst;
|
||||||
|
}
|
||||||
|
|
||||||
|
AstAnalysis analysis = analyzeAst(displayAst);
|
||||||
|
|
||||||
|
std::ostream* out = &std::cout;
|
||||||
|
std::ofstream outFile;
|
||||||
|
if (!args.outputFile.empty()) {
|
||||||
|
outFile.open(args.outputFile);
|
||||||
|
if (outFile.is_open()) out = &outFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
*out << "{\n"
|
||||||
|
<< " \"ast\":\n"
|
||||||
|
<< jsonIndent(2) << astToJson(displayAst, 2) << ",\n"
|
||||||
|
<< " \"analysis\": {\n"
|
||||||
|
<< analysisToJson(analysis) << "\n"
|
||||||
|
<< " }\n"
|
||||||
|
<< "}\n";
|
||||||
|
|
||||||
|
// Optimizasyon uyarılarını (W002 vb.) stderr'e yazdır
|
||||||
|
if (args.optimized) diag.printAll(std::cerr);
|
||||||
|
|
||||||
|
if (clonedAst) delete clonedAst;
|
||||||
|
delete ast;
|
||||||
|
for (auto* t : tokens) delete t;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // SAQUT_CLI_AST
|
||||||
|
|
@ -0,0 +1,59 @@
|
||||||
|
#ifndef SAQUT_CLI_CHECK
|
||||||
|
#define SAQUT_CLI_CHECK
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include "cli/args.hpp"
|
||||||
|
#include "tokenizer/tokenizer.hpp"
|
||||||
|
#include "parser/parser.hpp"
|
||||||
|
#include "symbol/symbol_table.hpp"
|
||||||
|
#include "symbol/symbol_collector.hpp"
|
||||||
|
#include "semantic/type_checker.hpp"
|
||||||
|
#include "semantic/structural_validator.hpp"
|
||||||
|
#include "diagnostic/diagnostic_engine.hpp"
|
||||||
|
#include "vendor/nlohmann/json.hpp"
|
||||||
|
|
||||||
|
inline int cmdCheck(const CliArgs& args) {
|
||||||
|
std::string filePath = inputFilePath(args);
|
||||||
|
std::string source = readSource(args);
|
||||||
|
if (source.empty()) return 1;
|
||||||
|
|
||||||
|
Tokenizer tokenizer;
|
||||||
|
auto tokens = tokenizer.scan(source, filePath);
|
||||||
|
|
||||||
|
Parser parser;
|
||||||
|
ASTNode* ast = parser.parse(tokens);
|
||||||
|
|
||||||
|
DiagnosticEngine diag;
|
||||||
|
|
||||||
|
if (!ast) {
|
||||||
|
diag.report("E000", SourceLocation{}, "AST üretilemedi");
|
||||||
|
nlohmann::json out;
|
||||||
|
out["file"] = filePath;
|
||||||
|
out["diagnostics"] = diag.toJsonObj();
|
||||||
|
std::cout << (args.compact ? out.dump() : out.dump(2)) << "\n";
|
||||||
|
for (auto* t : tokens) delete t;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
SymbolTable table;
|
||||||
|
SymbolCollector(table, diag).collect(ast);
|
||||||
|
|
||||||
|
// Sembol toplama hataları varsa tip denetimine geçme
|
||||||
|
// (çözümsüz semboller tip denetiminde sahte E003 üretir)
|
||||||
|
if (!diag.hasErrors()) {
|
||||||
|
TypeChecker(table, diag).check(ast);
|
||||||
|
StructuralValidator(diag).validate(ast);
|
||||||
|
}
|
||||||
|
|
||||||
|
nlohmann::json out;
|
||||||
|
out["file"] = filePath;
|
||||||
|
out["diagnostics"] = diag.toJsonObj();
|
||||||
|
|
||||||
|
std::cout << (args.compact ? out.dump() : out.dump(2)) << "\n";
|
||||||
|
|
||||||
|
delete ast;
|
||||||
|
for (auto* t : tokens) delete t;
|
||||||
|
return diag.hasErrors() ? 1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // SAQUT_CLI_CHECK
|
||||||
|
|
@ -0,0 +1,50 @@
|
||||||
|
#ifndef SAQUT_CLI_IR
|
||||||
|
#define SAQUT_CLI_IR
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include "cli/args.hpp"
|
||||||
|
#include "tokenizer/tokenizer.hpp"
|
||||||
|
#include "parser/parser.hpp"
|
||||||
|
#include "symbol/symbol_table.hpp"
|
||||||
|
#include "symbol/symbol_collector.hpp"
|
||||||
|
#include "diagnostic/diagnostic_engine.hpp"
|
||||||
|
#include "ir/ir_generator.hpp"
|
||||||
|
|
||||||
|
inline int cmdIr(const CliArgs& args) {
|
||||||
|
std::string filePath = inputFilePath(args);
|
||||||
|
std::string source = readSource(args);
|
||||||
|
if (source.empty()) return 1;
|
||||||
|
|
||||||
|
Tokenizer tokenizer;
|
||||||
|
auto tokens = tokenizer.scan(source, filePath);
|
||||||
|
|
||||||
|
Parser parser;
|
||||||
|
ASTNode* ast = parser.parse(tokens);
|
||||||
|
if (!ast) {
|
||||||
|
std::cerr << "Hata: AST üretilemedi\n";
|
||||||
|
for (auto* t : tokens) delete t;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
SymbolTable symbolTable;
|
||||||
|
DiagnosticEngine diag;
|
||||||
|
SymbolCollector(symbolTable, diag).collect(ast);
|
||||||
|
|
||||||
|
if (diag.hasErrors()) {
|
||||||
|
diag.printAll(std::cerr);
|
||||||
|
delete ast;
|
||||||
|
for (auto* t : tokens) delete t;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
IRGenerator irGenerator;
|
||||||
|
IRProgram program = irGenerator.generate(ast, symbolTable);
|
||||||
|
|
||||||
|
program.dump();
|
||||||
|
|
||||||
|
delete ast;
|
||||||
|
for (auto* t : tokens) delete t;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // SAQUT_CLI_IR
|
||||||
|
|
@ -0,0 +1,81 @@
|
||||||
|
// ============================================================================
|
||||||
|
// saQut CLI — run komutu
|
||||||
|
//
|
||||||
|
// Tam derleme + çalıştırma pipeline'ı:
|
||||||
|
// tokenize → parse → sembol topla → IR üret → VM çalıştır
|
||||||
|
//
|
||||||
|
// Başarı kriteri:
|
||||||
|
// build/saqut run file:examples/fibonacci.sqt
|
||||||
|
// → 55
|
||||||
|
// → 55
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
#ifndef SAQUT_CLI_RUN
|
||||||
|
#define SAQUT_CLI_RUN
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include "cli/args.hpp"
|
||||||
|
#include "tokenizer/tokenizer.hpp"
|
||||||
|
#include "parser/parser.hpp"
|
||||||
|
#include "symbol/symbol_table.hpp"
|
||||||
|
#include "symbol/symbol_collector.hpp"
|
||||||
|
#include "semantic/type_checker.hpp"
|
||||||
|
#include "semantic/structural_validator.hpp"
|
||||||
|
#include "diagnostic/diagnostic_engine.hpp"
|
||||||
|
#include "ir/ir_generator.hpp"
|
||||||
|
#include "vm/interpreter.hpp"
|
||||||
|
|
||||||
|
inline int cmdRun(const CliArgs& args) {
|
||||||
|
std::string filePath = inputFilePath(args);
|
||||||
|
std::string source = readSource(args);
|
||||||
|
if (source.empty()) return 1;
|
||||||
|
|
||||||
|
// ── Aşama 1: Tokenize ────────────────────────────────────────────────
|
||||||
|
Tokenizer tokenizer;
|
||||||
|
auto tokens = tokenizer.scan(source, filePath);
|
||||||
|
|
||||||
|
// ── Aşama 2: Parse ───────────────────────────────────────────────────
|
||||||
|
Parser parser;
|
||||||
|
ASTNode* ast = parser.parse(tokens);
|
||||||
|
if (!ast) {
|
||||||
|
std::cerr << "Hata: AST üretilemedi\n";
|
||||||
|
for (auto* t : tokens) delete t;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Aşama 3: Sembol toplama ───────────────────────────────────────────
|
||||||
|
// Identifier'ların resolvedSymbol'ü doldurulur — IR generator buna ihtiyaç duyar.
|
||||||
|
SymbolTable symbolTable;
|
||||||
|
DiagnosticEngine diag;
|
||||||
|
SymbolCollector(symbolTable, diag).collect(ast);
|
||||||
|
TypeChecker(symbolTable, diag).check(ast);
|
||||||
|
StructuralValidator(diag).validate(ast);
|
||||||
|
|
||||||
|
if (diag.hasErrors()) {
|
||||||
|
std::cerr << "Derleme hataları var, program çalıştırılamaz:\n";
|
||||||
|
diag.printAll(std::cerr);
|
||||||
|
delete ast;
|
||||||
|
for (auto* t : tokens) delete t;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Aşama 4: IR üretimi ───────────────────────────────────────────────
|
||||||
|
IRGenerator irGenerator;
|
||||||
|
IRProgram program = irGenerator.generate(ast, symbolTable);
|
||||||
|
|
||||||
|
// ── Aşama 5: VM çalıştırma ────────────────────────────────────────────
|
||||||
|
int exitCode = 0;
|
||||||
|
try {
|
||||||
|
Interpreter vm(program);
|
||||||
|
exitCode = vm.run();
|
||||||
|
} catch (const std::exception& e) {
|
||||||
|
std::cerr << "Çalışma zamanı hatası: " << e.what() << "\n";
|
||||||
|
exitCode = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
delete ast;
|
||||||
|
for (auto* t : tokens) delete t;
|
||||||
|
return exitCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // SAQUT_CLI_RUN
|
||||||
|
|
@ -0,0 +1,70 @@
|
||||||
|
// ============================================================================
|
||||||
|
// saQut CLI — symbols komutu (sembol tablosu — JSON çıktı, Faz 2)
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
#ifndef SAQUT_CLI_SYMBOLS
|
||||||
|
#define SAQUT_CLI_SYMBOLS
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include "cli/args.hpp"
|
||||||
|
#include "tokenizer/tokenizer.hpp"
|
||||||
|
#include "parser/parser.hpp"
|
||||||
|
#include "symbol/symbol_table.hpp"
|
||||||
|
#include "symbol/symbol_collector.hpp"
|
||||||
|
#include "diagnostic/diagnostic_engine.hpp"
|
||||||
|
#include "vendor/nlohmann/json.hpp"
|
||||||
|
|
||||||
|
inline int cmdSymbols(const CliArgs& args) {
|
||||||
|
std::string filePath = inputFilePath(args);
|
||||||
|
std::string source = readSource(args);
|
||||||
|
if (source.empty()) return 1;
|
||||||
|
|
||||||
|
Tokenizer tokenizer;
|
||||||
|
auto tokens = tokenizer.scan(source, filePath);
|
||||||
|
|
||||||
|
Parser parser;
|
||||||
|
ASTNode* ast = parser.parse(tokens);
|
||||||
|
|
||||||
|
SymbolTable table;
|
||||||
|
DiagnosticEngine diag;
|
||||||
|
|
||||||
|
if (ast) {
|
||||||
|
SymbolCollector(table, diag).collect(ast);
|
||||||
|
} else {
|
||||||
|
diag.report("E000", SourceLocation{}, "AST üretilemedi");
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── JSON çıktı ──────────────────────────────────────────────────────────
|
||||||
|
nlohmann::json out;
|
||||||
|
out["file"] = filePath;
|
||||||
|
|
||||||
|
nlohmann::json symArray = nlohmann::json::array();
|
||||||
|
for (Symbol* s : table.allSymbols()) {
|
||||||
|
if (s->isBuiltin) continue;
|
||||||
|
|
||||||
|
nlohmann::json refs = nlohmann::json::array();
|
||||||
|
for (const SourceLocation& r : s->references)
|
||||||
|
refs.push_back(r.toJsonObj());
|
||||||
|
|
||||||
|
symArray.push_back({
|
||||||
|
{"name", s->name},
|
||||||
|
{"kind", symbolKindName(s->kind)},
|
||||||
|
{"type", s->type.toString()},
|
||||||
|
{"typeDetail", s->type.toJsonObj()},
|
||||||
|
{"definition", s->definitionLoc.toJsonObj()},
|
||||||
|
{"referenceCount", static_cast<int>(s->references.size())},
|
||||||
|
{"references", refs},
|
||||||
|
{"isBuiltin", s->isBuiltin}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
out["symbols"] = symArray;
|
||||||
|
out["diagnostics"] = diag.toJsonObj();
|
||||||
|
|
||||||
|
std::cout << (args.compact ? out.dump() : out.dump(2)) << "\n";
|
||||||
|
|
||||||
|
delete ast;
|
||||||
|
for (auto* t : tokens) delete t;
|
||||||
|
return diag.hasErrors() ? 1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // SAQUT_CLI_SYMBOLS
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
// ============================================================================
|
||||||
|
// saQut CLI — tokens komutu
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
#ifndef SAQUT_CLI_TOKENS
|
||||||
|
#define SAQUT_CLI_TOKENS
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include "cli/args.hpp"
|
||||||
|
#include "tokenizer/tokenizer.hpp"
|
||||||
|
|
||||||
|
inline int cmdTokens(const CliArgs& args) {
|
||||||
|
std::string source = readSource(args);
|
||||||
|
if (source.empty()) return 1;
|
||||||
|
|
||||||
|
Tokenizer tokenizer;
|
||||||
|
auto tokens = tokenizer.scan(source, inputFilePath(args));
|
||||||
|
|
||||||
|
std::cout << "Tokenler (" << tokens.size() << " adet):\n";
|
||||||
|
for (auto* t : tokens) {
|
||||||
|
std::cout << " [" << t->gettype() << "] \"" << t->token << "\"\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto* t : tokens) delete t;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // SAQUT_CLI_TOKENS
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
#ifndef SAQUT_CORE_CONFIG
|
||||||
|
#define SAQUT_CORE_CONFIG
|
||||||
|
|
||||||
|
// Derleyici yapılandırması — hangi optimizasyon pass'lerinin çalışacağı.
|
||||||
|
struct CompilerConfig {
|
||||||
|
bool optConstantFolding = true;
|
||||||
|
bool optDeadCodeElim = true;
|
||||||
|
int maxFixpointRounds = 10;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // SAQUT_CORE_CONFIG
|
||||||
|
|
@ -0,0 +1,83 @@
|
||||||
|
// ============================================================================
|
||||||
|
// saQut Compiler — Kaynak Kod Konum Yapısı
|
||||||
|
// ============================================================================
|
||||||
|
//
|
||||||
|
// DİZİN: src/core/location.hpp
|
||||||
|
// KATMAN: Katman 0 — Tüm katmanlar tarafından kullanılır
|
||||||
|
// BAĞIMLI: Yok (sadece <string>)
|
||||||
|
//
|
||||||
|
// AMAÇ:
|
||||||
|
// Her token ve AST düğümünün kaynak koddaki tam konumunu tutar.
|
||||||
|
// "Hata nerede?" ve "Kullanıcı imleci nerede?" sorularına cevap verir.
|
||||||
|
//
|
||||||
|
// ALANLAR:
|
||||||
|
// filePath : Kaynak dosyanın yolu (bilinmiyorsa boş string)
|
||||||
|
// line : 1-tabanlı satır numarası
|
||||||
|
// column : 1-tabanlı sütun numarası
|
||||||
|
// offset : 0-tabanlı karakter offset'i (dosya başından itibaren)
|
||||||
|
//
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
#ifndef SAQUT_CORE_LOCATION
|
||||||
|
#define SAQUT_CORE_LOCATION
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include "vendor/nlohmann/json.hpp"
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// SourceLocation — Kaynak Koddaki Bir Nokta
|
||||||
|
// ============================================================================
|
||||||
|
//
|
||||||
|
// KULLANIM:
|
||||||
|
// SourceLocation loc{"test.sqt", 5, 10, 134};
|
||||||
|
// std::cout << loc.toString(); // "test.sqt:5:10"
|
||||||
|
// std::cout << loc.shortString(); // "5:10"
|
||||||
|
//
|
||||||
|
// Varsayılan kurucu: geçersiz bir konum üretir (line=0, column=0, offset=-1).
|
||||||
|
// isValid() ile kontrol edilebilir.
|
||||||
|
//
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
struct SourceLocation {
|
||||||
|
std::string filePath;
|
||||||
|
int line = 0; // 1-tabanlı, 0 = geçersiz
|
||||||
|
int column = 0; // 1-tabanlı, 0 = geçersiz
|
||||||
|
int offset = -1; // 0-tabanlı, -1 = geçersiz
|
||||||
|
|
||||||
|
SourceLocation() = default;
|
||||||
|
|
||||||
|
SourceLocation(std::string file, int line, int col, int off)
|
||||||
|
: filePath(std::move(file)), line(line), column(col), offset(off) {}
|
||||||
|
|
||||||
|
// Geçerli bir konum mu?
|
||||||
|
bool isValid() const {
|
||||||
|
return line > 0 && column > 0 && offset >= 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tam konum: "dosya.sqt:5:10"
|
||||||
|
std::string toString() const {
|
||||||
|
if (!isValid()) return "<invalid>";
|
||||||
|
return filePath + ":" + std::to_string(line) + ":" + std::to_string(column);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Kısa konum: "5:10"
|
||||||
|
std::string shortString() const {
|
||||||
|
if (!isValid()) return "?:?";
|
||||||
|
return std::to_string(line) + ":" + std::to_string(column);
|
||||||
|
}
|
||||||
|
|
||||||
|
// JSON formatı: {"file":"...","line":5,"column":10,"offset":134}
|
||||||
|
nlohmann::json toJsonObj() const {
|
||||||
|
if (!isValid()) return nullptr;
|
||||||
|
return {
|
||||||
|
{"file", filePath},
|
||||||
|
{"line", line},
|
||||||
|
{"column", column},
|
||||||
|
{"offset", offset}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string toJson() const { return toJsonObj().dump(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // SAQUT_CORE_LOCATION
|
||||||
|
|
@ -0,0 +1,69 @@
|
||||||
|
#include "core/sourcefile.hpp"
|
||||||
|
|
||||||
|
void SourceFile::setText(const std::string& path, const std::string& source) {
|
||||||
|
filePath = path;
|
||||||
|
text = source;
|
||||||
|
computeLineStarts();
|
||||||
|
}
|
||||||
|
|
||||||
|
int SourceFile::lineCount() const {
|
||||||
|
return static_cast<int>(lineStarts.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string SourceFile::getLine(int line) const {
|
||||||
|
if (line < 1 || line > lineCount()) return "";
|
||||||
|
int start = lineStarts[line - 1];
|
||||||
|
int end;
|
||||||
|
if (line < lineCount()) {
|
||||||
|
end = lineStarts[line] - 1; // Satır sonu (\n) hariç
|
||||||
|
// \r\n varsa bir karakter daha geri
|
||||||
|
if (end > start && text[end - 1] == '\r') end--;
|
||||||
|
} else {
|
||||||
|
end = static_cast<int>(text.length());
|
||||||
|
}
|
||||||
|
return text.substr(start, end - start);
|
||||||
|
}
|
||||||
|
|
||||||
|
SourceLocation SourceFile::offsetToLocation(int offset) const {
|
||||||
|
// Geçersiz offset kontrolü
|
||||||
|
if (offset < 0 || offset > static_cast<int>(text.length())) {
|
||||||
|
return SourceLocation{filePath, 0, 0, -1};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Binary search: offset'in hangi satıra ait olduğunu bul
|
||||||
|
// lineStarts içinde offset'ten büyük ilk elemanı bul
|
||||||
|
auto it = std::upper_bound(lineStarts.begin(), lineStarts.end(), offset);
|
||||||
|
int lineIndex = static_cast<int>(it - lineStarts.begin()) - 1;
|
||||||
|
|
||||||
|
// lineIndex geçerli değilse
|
||||||
|
if (lineIndex < 0) {
|
||||||
|
lineIndex = 0;
|
||||||
|
} else if (lineIndex >= static_cast<int>(lineStarts.size())) {
|
||||||
|
lineIndex = static_cast<int>(lineStarts.size()) - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int lineStart = lineStarts[lineIndex];
|
||||||
|
int line = lineIndex + 1; // 1-tabanlı
|
||||||
|
int column = offset - lineStart + 1; // 1-tabanlı
|
||||||
|
|
||||||
|
return SourceLocation{filePath, line, column, offset};
|
||||||
|
}
|
||||||
|
|
||||||
|
SourceFile::LocationRange SourceFile::rangeFromOffsets(int startOffset, int endOffset) const {
|
||||||
|
return {offsetToLocation(startOffset), offsetToLocation(endOffset)};
|
||||||
|
}
|
||||||
|
|
||||||
|
void SourceFile::computeLineStarts() {
|
||||||
|
lineStarts.clear();
|
||||||
|
lineStarts.push_back(0); // 1. satır offset 0
|
||||||
|
|
||||||
|
for (int i = 0; i < static_cast<int>(text.length()); i++) {
|
||||||
|
if (text[i] == '\n') {
|
||||||
|
// \r\n kontrolü: \r'yi atla, \n'den sonraki karakter yeni satır
|
||||||
|
int nextStart = i + 1;
|
||||||
|
if (nextStart < static_cast<int>(text.length())) {
|
||||||
|
lineStarts.push_back(nextStart);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,84 @@
|
||||||
|
// ============================================================================
|
||||||
|
// saQut Compiler — Kaynak Kod Yöneticisi
|
||||||
|
// ============================================================================
|
||||||
|
//
|
||||||
|
// DİZİN: src/core/sourcefile.hpp
|
||||||
|
// KATMAN: Katman 0 — Tüm katmanlar tarafından kullanılır
|
||||||
|
// BAĞIMLI: core/location.hpp
|
||||||
|
//
|
||||||
|
// AMAÇ:
|
||||||
|
// Kaynak kodun tamamını ve satır başı offset'lerini tutar.
|
||||||
|
// offset → (line, column) dönüşümü yapar.
|
||||||
|
//
|
||||||
|
// TASARIM:
|
||||||
|
// lineStarts vektörü, her satırın ilk karakterinin offset'ini tutar:
|
||||||
|
// lineStarts[0] = 0 (1. satır, offset 0)
|
||||||
|
// lineStarts[1] = 15 (2. satır, offset 15)
|
||||||
|
// lineStarts[2] = 32 (3. satır, offset 32)
|
||||||
|
//
|
||||||
|
// offsetToLocation() bu dizide binary search yaparak O(log n)'de line/column
|
||||||
|
// bulur. Line-start dizisi bir kere setText()'te O(n)'de hesaplanır.
|
||||||
|
//
|
||||||
|
// PERFORMANS:
|
||||||
|
// setText() : O(n) — line-start dizisi bir kere kurulur
|
||||||
|
// offsetToLocation() : O(log n) — binary search
|
||||||
|
// Bellek : O(n) — lineStarts (en fazla n eleman, her satır için bir int)
|
||||||
|
//
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
#ifndef SAQUT_CORE_SOURCEFILE
|
||||||
|
#define SAQUT_CORE_SOURCEFILE
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include "core/location.hpp"
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// SourceFile — Kaynak Kod Yöneticisi
|
||||||
|
// ============================================================================
|
||||||
|
//
|
||||||
|
// KULLANIM:
|
||||||
|
// SourceFile sf;
|
||||||
|
// sf.setText("deneme.sqt", "int x = 5;\nreturn x;\n");
|
||||||
|
// SourceLocation loc = sf.offsetToLocation(10); // 1:10 (2. satır)
|
||||||
|
//
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
class SourceFile {
|
||||||
|
public:
|
||||||
|
std::string filePath; // Kaynak dosyanın yolu
|
||||||
|
std::string text; // Kaynak kodun tamamı
|
||||||
|
std::vector<int> lineStarts; // Her satırın başlangıç offset'i
|
||||||
|
|
||||||
|
SourceFile() = default;
|
||||||
|
|
||||||
|
// text verisini yeni satır dizisini de hesapla
|
||||||
|
void setText(const std::string& path, const std::string& source);
|
||||||
|
|
||||||
|
// Kaynak kodun toplam satır sayısı
|
||||||
|
int lineCount() const;
|
||||||
|
|
||||||
|
// Belirtilen offset'teki satırın tam metnini döndür
|
||||||
|
std::string getLine(int line) const;
|
||||||
|
|
||||||
|
// Offset'ten (line, column) dönüşümü
|
||||||
|
// Binary search ile O(log n)
|
||||||
|
SourceLocation offsetToLocation(int offset) const;
|
||||||
|
|
||||||
|
// Bir aralığın başlangıç ve bitiş konumlarını döndür
|
||||||
|
struct LocationRange {
|
||||||
|
SourceLocation start;
|
||||||
|
SourceLocation end;
|
||||||
|
};
|
||||||
|
|
||||||
|
LocationRange rangeFromOffsets(int startOffset, int endOffset) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
// lineStarts vektörünü hesapla
|
||||||
|
// Her \n karakterinden sonraki offset bir sonraki satırın başlangıcıdır
|
||||||
|
// İlk satır her zaman offset 0'dan başlar
|
||||||
|
void computeLineStarts();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // SAQUT_CORE_SOURCEFILE
|
||||||
|
|
@ -0,0 +1,257 @@
|
||||||
|
// ============================================================================
|
||||||
|
// saQut Compiler — Tip Sistemi (Type System)
|
||||||
|
// ============================================================================
|
||||||
|
//
|
||||||
|
// DİZİN: src/core/type.hpp
|
||||||
|
// KATMAN: Katman 0 — Tüm analiz katmanları tarafından kullanılır
|
||||||
|
// BAĞIMLI: Yok (sadece <string>, <vector>, <memory>)
|
||||||
|
// KULLANAN: Sembol tablosu (Faz 2), tip denetleyici (Faz 3), optimizasyon (Faz 4)
|
||||||
|
//
|
||||||
|
// AMAÇ:
|
||||||
|
// Kaynak koddaki her ifadenin/sembolün veri tipini temsil eder. Derleyicinin
|
||||||
|
// "bu değer ne?" sorusuna verdiği yapısal cevaptır. Tip, makine-okur (toJson)
|
||||||
|
// ve insan-okur (toString) olarak dışa açıktır — "veri birincil, metin bir
|
||||||
|
// görünümdür" ilkesine uyar (bkz. readme → Tasarım felsefesi).
|
||||||
|
//
|
||||||
|
// TİP TÜRLERİ (TypeKind):
|
||||||
|
// Primitive : int, float, double, char, string, bool, void
|
||||||
|
// Array : eleman tipi taşır (örn. int[])
|
||||||
|
// Struct : struct adı taşır (örn. struct Point)
|
||||||
|
// Function : dönüş tipi + parametre tipleri taşır
|
||||||
|
// Error : hatalı/çözümlenememiş tip — ardışık sahte hataları bastırmak için
|
||||||
|
// (tip denetleyici, operandı Error olan ifadede yeni hata üretmez)
|
||||||
|
//
|
||||||
|
// NOT (kasıtlı sadelik): Gizli tip dönüşümü YOKTUR (ADR-010). equals() yapısal
|
||||||
|
// ve katıdır; "int, float'a uyar mı?" gibi kurallar tip denetleyicinin işidir,
|
||||||
|
// bu dosyanın değil. Tamsayı literalinin bağlama-göre tiplenmesi de (ADR-010)
|
||||||
|
// Faz 3'te ele alınır.
|
||||||
|
//
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
#ifndef SAQUT_CORE_TYPE
|
||||||
|
#define SAQUT_CORE_TYPE
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include <memory>
|
||||||
|
#include "vendor/nlohmann/json.hpp"
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// Enum'lar
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
enum class PrimitiveKind { Int, Float, Double, Char, String, Bool, Void };
|
||||||
|
|
||||||
|
enum class TypeKind { Primitive, Array, Struct, Function, Error };
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// Type — Bir veri tipi
|
||||||
|
// ============================================================================
|
||||||
|
//
|
||||||
|
// KULLANIM:
|
||||||
|
// Type a = Type::Int(); // int
|
||||||
|
// Type b = Type::array(Type::Int()); // int[]
|
||||||
|
// Type c = Type::function(Type::Int(), {Type::Int(), Type::Int()}); // fn(int,int)->int
|
||||||
|
// Type d = Type::structType("Point"); // struct Point
|
||||||
|
// Type e = Type::error(); // <error>
|
||||||
|
//
|
||||||
|
// a.equals(Type::Int()); // true
|
||||||
|
// a.equals(b); // false
|
||||||
|
// a.toString(); // "int"
|
||||||
|
// b.toString(); // "int[]"
|
||||||
|
// c.toJson(); // {"kind":"function",...}
|
||||||
|
//
|
||||||
|
// İç içe tipler (array elemanı, fonksiyon dönüşü) shared_ptr ile tutulur:
|
||||||
|
// Type değer-semantiğiyle kopyalanabilir kalır ama özyinelemeli olabilir.
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
struct Type {
|
||||||
|
TypeKind kind = TypeKind::Error;
|
||||||
|
|
||||||
|
PrimitiveKind prim = PrimitiveKind::Void; // kind == Primitive
|
||||||
|
std::shared_ptr<Type> elementType; // kind == Array
|
||||||
|
std::shared_ptr<Type> returnType; // kind == Function
|
||||||
|
std::vector<Type> paramTypes; // kind == Function
|
||||||
|
std::string structName; // kind == Struct
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------ //
|
||||||
|
// Factory'ler
|
||||||
|
// ------------------------------------------------------------------ //
|
||||||
|
static Type primitive(PrimitiveKind p) {
|
||||||
|
Type t;
|
||||||
|
t.kind = TypeKind::Primitive;
|
||||||
|
t.prim = p;
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
static Type Int() { return primitive(PrimitiveKind::Int); }
|
||||||
|
static Type Float() { return primitive(PrimitiveKind::Float); }
|
||||||
|
static Type Double() { return primitive(PrimitiveKind::Double); }
|
||||||
|
static Type Char() { return primitive(PrimitiveKind::Char); }
|
||||||
|
static Type String() { return primitive(PrimitiveKind::String); }
|
||||||
|
static Type Bool() { return primitive(PrimitiveKind::Bool); }
|
||||||
|
static Type Void() { return primitive(PrimitiveKind::Void); }
|
||||||
|
|
||||||
|
static Type array(Type elem) {
|
||||||
|
Type t;
|
||||||
|
t.kind = TypeKind::Array;
|
||||||
|
t.elementType = std::make_shared<Type>(std::move(elem));
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
static Type function(Type ret, std::vector<Type> params) {
|
||||||
|
Type t;
|
||||||
|
t.kind = TypeKind::Function;
|
||||||
|
t.returnType = std::make_shared<Type>(std::move(ret));
|
||||||
|
t.paramTypes = std::move(params);
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
static Type structType(std::string name) {
|
||||||
|
Type t;
|
||||||
|
t.kind = TypeKind::Struct;
|
||||||
|
t.structName = std::move(name);
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
static Type error() {
|
||||||
|
return Type{}; // varsayılan = Error
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------ //
|
||||||
|
// Yüklemler (predicates)
|
||||||
|
// ------------------------------------------------------------------ //
|
||||||
|
bool isError() const { return kind == TypeKind::Error; }
|
||||||
|
bool isPrimitive() const { return kind == TypeKind::Primitive; }
|
||||||
|
bool isArray() const { return kind == TypeKind::Array; }
|
||||||
|
bool isStruct() const { return kind == TypeKind::Struct; }
|
||||||
|
bool isFunction() const { return kind == TypeKind::Function; }
|
||||||
|
bool isVoid() const { return kind == TypeKind::Primitive && prim == PrimitiveKind::Void; }
|
||||||
|
|
||||||
|
// Aritmetik/karşılaştırma operatörlerine uygun sayısal tip mi?
|
||||||
|
bool isNumeric() const {
|
||||||
|
return kind == TypeKind::Primitive &&
|
||||||
|
(prim == PrimitiveKind::Int ||
|
||||||
|
prim == PrimitiveKind::Float ||
|
||||||
|
prim == PrimitiveKind::Double);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------ //
|
||||||
|
// equals — Yapısal eşitlik (katı; gizli dönüşüm yok, ADR-010)
|
||||||
|
// ------------------------------------------------------------------ //
|
||||||
|
bool equals(const Type& o) const {
|
||||||
|
if (kind != o.kind) return false;
|
||||||
|
switch (kind) {
|
||||||
|
case TypeKind::Primitive:
|
||||||
|
return prim == o.prim;
|
||||||
|
case TypeKind::Array:
|
||||||
|
return elementType && o.elementType &&
|
||||||
|
elementType->equals(*o.elementType);
|
||||||
|
case TypeKind::Struct:
|
||||||
|
return structName == o.structName;
|
||||||
|
case TypeKind::Function: {
|
||||||
|
if (!returnType || !o.returnType) return false;
|
||||||
|
if (!returnType->equals(*o.returnType)) return false;
|
||||||
|
if (paramTypes.size() != o.paramTypes.size()) return false;
|
||||||
|
for (size_t i = 0; i < paramTypes.size(); ++i)
|
||||||
|
if (!paramTypes[i].equals(o.paramTypes[i])) return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
case TypeKind::Error:
|
||||||
|
// Error == Error: ardışık sahte hataların bastırılması tip
|
||||||
|
// denetleyicinin sorumluluğundadır (operandı Error ise hata üretme).
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false; // erişilemez (tüm enum değerleri kapsandı)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------ //
|
||||||
|
// İsim yardımcıları
|
||||||
|
// ------------------------------------------------------------------ //
|
||||||
|
static const char* primName(PrimitiveKind p) {
|
||||||
|
switch (p) {
|
||||||
|
case PrimitiveKind::Int: return "int";
|
||||||
|
case PrimitiveKind::Float: return "float";
|
||||||
|
case PrimitiveKind::Double: return "double";
|
||||||
|
case PrimitiveKind::Char: return "char";
|
||||||
|
case PrimitiveKind::String: return "string";
|
||||||
|
case PrimitiveKind::Bool: return "bool";
|
||||||
|
case PrimitiveKind::Void: return "void";
|
||||||
|
}
|
||||||
|
return "?";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bir tip adından (parser tipleri string olarak tutar) primitif Type üretir.
|
||||||
|
// Bilinen primitif değilse Error döner — bilinmeyen tip adının teşhisi
|
||||||
|
// (E007) çağıranın (Faz 2/3) işidir; bu fonksiyon sessizce Error verir.
|
||||||
|
static Type fromName(const std::string& n) {
|
||||||
|
if (n == "int") return Int();
|
||||||
|
if (n == "float") return Float();
|
||||||
|
if (n == "double") return Double();
|
||||||
|
if (n == "char") return Char();
|
||||||
|
if (n == "string") return String();
|
||||||
|
if (n == "bool") return Bool();
|
||||||
|
if (n == "void") return Void();
|
||||||
|
return error();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------ //
|
||||||
|
// toString — İnsan-okur ("int", "int[]", "fn(int,int)->int")
|
||||||
|
// ------------------------------------------------------------------ //
|
||||||
|
std::string toString() const {
|
||||||
|
switch (kind) {
|
||||||
|
case TypeKind::Primitive:
|
||||||
|
return primName(prim);
|
||||||
|
case TypeKind::Array:
|
||||||
|
return (elementType ? elementType->toString() : "<?>") + "[]";
|
||||||
|
case TypeKind::Struct:
|
||||||
|
return "struct " + structName;
|
||||||
|
case TypeKind::Function: {
|
||||||
|
std::string s = "fn(";
|
||||||
|
for (size_t i = 0; i < paramTypes.size(); ++i) {
|
||||||
|
if (i) s += ",";
|
||||||
|
s += paramTypes[i].toString();
|
||||||
|
}
|
||||||
|
s += ")->";
|
||||||
|
s += returnType ? returnType->toString() : "<?>";
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
case TypeKind::Error:
|
||||||
|
return "<error>";
|
||||||
|
}
|
||||||
|
return "<?>";
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------ //
|
||||||
|
// toJson — Makine-okur (cam ilkesi: her tip dışarıdan sorgulanabilir)
|
||||||
|
// ------------------------------------------------------------------ //
|
||||||
|
nlohmann::json toJsonObj() const {
|
||||||
|
nlohmann::json j;
|
||||||
|
switch (kind) {
|
||||||
|
case TypeKind::Primitive:
|
||||||
|
j["kind"] = "primitive";
|
||||||
|
j["name"] = primName(prim);
|
||||||
|
break;
|
||||||
|
case TypeKind::Array:
|
||||||
|
j["kind"] = "array";
|
||||||
|
j["element"] = elementType ? elementType->toJsonObj() : nullptr;
|
||||||
|
break;
|
||||||
|
case TypeKind::Struct:
|
||||||
|
j["kind"] = "struct";
|
||||||
|
j["name"] = structName;
|
||||||
|
break;
|
||||||
|
case TypeKind::Function: {
|
||||||
|
j["kind"] = "function";
|
||||||
|
j["returns"] = returnType ? returnType->toJsonObj() : nullptr;
|
||||||
|
nlohmann::json params = nlohmann::json::array();
|
||||||
|
for (const auto& p : paramTypes) params.push_back(p.toJsonObj());
|
||||||
|
j["params"] = params;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case TypeKind::Error:
|
||||||
|
j["kind"] = "error";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return j;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string toJson() const { return toJsonObj().dump(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // SAQUT_CORE_TYPE
|
||||||
|
|
@ -0,0 +1,164 @@
|
||||||
|
// ============================================================================
|
||||||
|
// saQut Compiler — Tanılama (Diagnostic) Veri Yapıları + Hata Kataloğu
|
||||||
|
// ============================================================================
|
||||||
|
//
|
||||||
|
// DİZİN: src/diagnostic/diagnostic.hpp
|
||||||
|
// KATMAN: Katman 0 — Tüm analiz katmanları tarafından kullanılır
|
||||||
|
// BAĞIMLI: src/core/location.hpp
|
||||||
|
// KULLANAN: DiagnosticEngine, sembol toplayıcı (Faz 2), tip denetleyici (Faz 3)
|
||||||
|
//
|
||||||
|
// AMAÇ:
|
||||||
|
// Derleme sırasında bulunan hata/uyarıları YAPISAL veri olarak temsil eder.
|
||||||
|
// "Veri birincil, insan-okur metin bir görünümdür" (readme → Tasarım felsefesi):
|
||||||
|
// bir Diagnostic; seviye + kod + konum + mesaj taşır; ekrana basılan satır
|
||||||
|
// bunun yalnızca bir render'ıdır. Bu sayede aynı tanı LSP, AI veya `saqut
|
||||||
|
// explain` tarafından da tüketilebilir.
|
||||||
|
//
|
||||||
|
// HATA KATALOĞU (baştan sabitlenir — yeni kodlar buraya eklenir):
|
||||||
|
// E001 Tanımsız değişken/isim (declare-before-use ihlali dâhil) Faz 2/3
|
||||||
|
// E002 Aynı scope'ta çift tanım Faz 2
|
||||||
|
// E003 Tip uyuşmazlığı (gizli dönüşüm yok, ADR-010) 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
|
||||||
|
// E010 Özyinelemeli/döngüsel struct (by-value çevrim → sonsuz boyut) Faz 2/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
|
||||||
|
//
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
#ifndef SAQUT_DIAGNOSTIC_DIAGNOSTIC
|
||||||
|
#define SAQUT_DIAGNOSTIC_DIAGNOSTIC
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include "core/location.hpp"
|
||||||
|
#include "tools.hpp" // jsonEscape — TEK tanım (tools.hpp); çakışmayı önler
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// DiagLevel — Tanı seviyesi
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
enum class DiagLevel { Error, Warning, Note, Hint };
|
||||||
|
|
||||||
|
inline const char* diagLevelName(DiagLevel l) {
|
||||||
|
switch (l) {
|
||||||
|
case DiagLevel::Error: return "error";
|
||||||
|
case DiagLevel::Warning: return "warning";
|
||||||
|
case DiagLevel::Note: return "note";
|
||||||
|
case DiagLevel::Hint: return "hint";
|
||||||
|
}
|
||||||
|
return "?";
|
||||||
|
}
|
||||||
|
|
||||||
|
// İnsan-okur çıktı için Türkçe karşılık
|
||||||
|
inline const char* diagLevelNameTr(DiagLevel l) {
|
||||||
|
switch (l) {
|
||||||
|
case DiagLevel::Error: return "hata";
|
||||||
|
case DiagLevel::Warning: return "uyarı";
|
||||||
|
case DiagLevel::Note: return "not";
|
||||||
|
case DiagLevel::Hint: return "ipucu";
|
||||||
|
}
|
||||||
|
return "?";
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOT: jsonEscape() tools.hpp'de tanımlıdır (tek tanım — ODR çakışması olmaz).
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// Diagnostic — Tek bir tanı (hata/uyarı/not/ipucu)
|
||||||
|
// ============================================================================
|
||||||
|
//
|
||||||
|
// KULLANIM:
|
||||||
|
// Diagnostic d{DiagLevel::Error, "E003", loc, "int'e string atanamaz"};
|
||||||
|
// d.hint = "açık dönüşüm gerekiyor";
|
||||||
|
// std::cout << d.toJson();
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
struct Diagnostic {
|
||||||
|
DiagLevel level = DiagLevel::Error;
|
||||||
|
std::string code; // "E003" (katalog kodu; boş olabilir)
|
||||||
|
SourceLocation loc; // hatanın kaynak koddaki yeri
|
||||||
|
std::string message; // bağlama özel açıklama
|
||||||
|
std::string hint; // opsiyonel "şunu dene" önerisi
|
||||||
|
|
||||||
|
nlohmann::json toJsonObj() const {
|
||||||
|
nlohmann::json j;
|
||||||
|
j["level"] = diagLevelName(level);
|
||||||
|
j["code"] = code;
|
||||||
|
j["location"] = loc.toJsonObj();
|
||||||
|
j["message"] = message;
|
||||||
|
if (!hint.empty()) j["hint"] = hint;
|
||||||
|
return j;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string toJson() const { return toJsonObj().dump(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// Hata Kataloğu — kod → (seviye, kanonik başlık)
|
||||||
|
// ============================================================================
|
||||||
|
//
|
||||||
|
// Bağlama özel mesaj report sırasında verilir; buradaki başlık, kodun GENEL
|
||||||
|
// anlamıdır (ileride `saqut explain E003` bunu kullanabilir, #107/#98).
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
struct DiagInfo {
|
||||||
|
const char* code;
|
||||||
|
DiagLevel level;
|
||||||
|
const char* title;
|
||||||
|
};
|
||||||
|
|
||||||
|
inline const std::vector<DiagInfo>& diagnosticCatalog() {
|
||||||
|
static const std::vector<DiagInfo> catalog = {
|
||||||
|
{"E001", DiagLevel::Error, "Tanımsız değişken/isim"},
|
||||||
|
{"E002", DiagLevel::Error, "Aynı scope'ta çift tanım"},
|
||||||
|
{"E003", DiagLevel::Error, "Tip uyuşmazlığı"},
|
||||||
|
{"E004", DiagLevel::Error, "Döngü/switch dışı break/continue"},
|
||||||
|
{"E005", DiagLevel::Error, "Fonksiyon dışı return"},
|
||||||
|
{"E006", DiagLevel::Error, "Return tipi imzaya uymuyor"},
|
||||||
|
{"E007", DiagLevel::Error, "Tanımsız tip"},
|
||||||
|
{"E008", DiagLevel::Error, "Fonksiyon çağrısı argümanı uyuşmuyor"},
|
||||||
|
{"E009", DiagLevel::Error, "Array boyutu sabit değil / geçersiz"},
|
||||||
|
{"E010", DiagLevel::Error, "Özyinelemeli/döngüsel struct tanımı"},
|
||||||
|
{"W001", DiagLevel::Warning, "Kullanılmayan değişken"},
|
||||||
|
{"W002", DiagLevel::Warning, "Sıfıra bölme (sabit ifade)"},
|
||||||
|
{"W003", DiagLevel::Warning, "Erişilemez (ölü) kod"},
|
||||||
|
{"W004", DiagLevel::Warning, "Örtük sayısal genişletme (widening)"},
|
||||||
|
};
|
||||||
|
return catalog;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Kod kataloğda var mı? (yoksa nullptr)
|
||||||
|
inline const DiagInfo* findDiag(const std::string& code) {
|
||||||
|
for (const auto& d : diagnosticCatalog())
|
||||||
|
if (code == d.code) return &d;
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bir koddan Diagnostic üretir; seviye kataloğdan çözülür (yoksa: E→Error,
|
||||||
|
// W→Warning, diğer→Note). Bağlama özel mesajı çağıran verir.
|
||||||
|
inline Diagnostic makeDiagnostic(const std::string& code,
|
||||||
|
const SourceLocation& loc,
|
||||||
|
const std::string& message,
|
||||||
|
const std::string& hint = "") {
|
||||||
|
DiagLevel level = DiagLevel::Note;
|
||||||
|
if (const DiagInfo* info = findDiag(code)) {
|
||||||
|
level = info->level;
|
||||||
|
} else if (!code.empty()) {
|
||||||
|
if (code[0] == 'E') level = DiagLevel::Error;
|
||||||
|
else if (code[0] == 'W') level = DiagLevel::Warning;
|
||||||
|
}
|
||||||
|
Diagnostic d;
|
||||||
|
d.level = level;
|
||||||
|
d.code = code;
|
||||||
|
d.loc = loc;
|
||||||
|
d.message = message;
|
||||||
|
d.hint = hint;
|
||||||
|
return d;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // SAQUT_DIAGNOSTIC_DIAGNOSTIC
|
||||||
|
|
@ -0,0 +1,116 @@
|
||||||
|
// ============================================================================
|
||||||
|
// saQut Compiler — Tanılama Motoru (DiagnosticEngine)
|
||||||
|
// ============================================================================
|
||||||
|
//
|
||||||
|
// DİZİN: src/diagnostic/diagnostic_engine.hpp
|
||||||
|
// KATMAN: Katman 0 — Tüm analiz katmanları tarafından kullanılır
|
||||||
|
// BAĞIMLI: src/diagnostic/diagnostic.hpp
|
||||||
|
// KULLANAN: sembol toplayıcı (Faz 2), tip denetleyici (Faz 3), pipeline (main)
|
||||||
|
//
|
||||||
|
// AMAÇ:
|
||||||
|
// Derleme boyunca üretilen tüm Diagnostic'leri EKLENME SIRASIYLA biriktirir.
|
||||||
|
// İlk hatada DURMAZ (ADR-013): bütün hatalar toplanır, faz sonunda topluca
|
||||||
|
// raporlanır; durdurma kararını pipeline verir (hasErrors()).
|
||||||
|
//
|
||||||
|
// İki çıktı yüzü vardır — aynı veriden:
|
||||||
|
// printAll() → insan-okur (terminal)
|
||||||
|
// toJson() → makine-okur (LSP / AI / araçlar)
|
||||||
|
//
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
#ifndef SAQUT_DIAGNOSTIC_ENGINE
|
||||||
|
#define SAQUT_DIAGNOSTIC_ENGINE
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include <ostream>
|
||||||
|
#include "diagnostic/diagnostic.hpp"
|
||||||
|
#include "vendor/nlohmann/json.hpp"
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// DiagnosticEngine
|
||||||
|
// ============================================================================
|
||||||
|
//
|
||||||
|
// KULLANIM:
|
||||||
|
// DiagnosticEngine diag;
|
||||||
|
// diag.report(makeDiagnostic("E001", loc, "x tanımsız"));
|
||||||
|
// diag.report(DiagLevel::Warning, "W001", loc2, "y kullanılmıyor");
|
||||||
|
// if (diag.hasErrors()) diag.printAll(std::cerr);
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
class DiagnosticEngine {
|
||||||
|
public:
|
||||||
|
// --- Ekleme ---
|
||||||
|
void report(const Diagnostic& d) {
|
||||||
|
diagnostics_.push_back(d);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Kolaylık: koddan üret + ekle (seviye kataloğdan çözülür)
|
||||||
|
void report(const std::string& code,
|
||||||
|
const SourceLocation& loc,
|
||||||
|
const std::string& message,
|
||||||
|
const std::string& hint = "") {
|
||||||
|
diagnostics_.push_back(makeDiagnostic(code, loc, message, hint));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Kolaylık: seviyeyi açıkça vererek
|
||||||
|
void report(DiagLevel level,
|
||||||
|
const std::string& code,
|
||||||
|
const SourceLocation& loc,
|
||||||
|
const std::string& message,
|
||||||
|
const std::string& hint = "") {
|
||||||
|
Diagnostic d;
|
||||||
|
d.level = level; d.code = code; d.loc = loc; d.message = message; d.hint = hint;
|
||||||
|
diagnostics_.push_back(d);
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Sorgu ---
|
||||||
|
bool hasErrors() const { return errorCount() > 0; }
|
||||||
|
|
||||||
|
int errorCount() const { return countLevel(DiagLevel::Error); }
|
||||||
|
int warningCount() const { return countLevel(DiagLevel::Warning); }
|
||||||
|
int count() const { return static_cast<int>(diagnostics_.size()); }
|
||||||
|
bool empty() const { return diagnostics_.empty(); }
|
||||||
|
|
||||||
|
const std::vector<Diagnostic>& all() const { return diagnostics_; }
|
||||||
|
|
||||||
|
void clear() { diagnostics_.clear(); }
|
||||||
|
|
||||||
|
// --- İnsan-okur çıktı (ekleme sırasıyla) ---
|
||||||
|
void printAll(std::ostream& os) const {
|
||||||
|
for (const auto& d : diagnostics_) {
|
||||||
|
os << d.loc.toString() << ": "
|
||||||
|
<< diagLevelNameTr(d.level) << " [" << d.code << "]: "
|
||||||
|
<< d.message << "\n";
|
||||||
|
if (!d.hint.empty())
|
||||||
|
os << " ipucu: " << d.hint << "\n";
|
||||||
|
}
|
||||||
|
os << "— " << errorCount() << " hata, " << warningCount() << " uyarı\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Makine-okur çıktı ---
|
||||||
|
nlohmann::json toJsonObj() const {
|
||||||
|
nlohmann::json items = nlohmann::json::array();
|
||||||
|
for (const auto& d : diagnostics_)
|
||||||
|
items.push_back(d.toJsonObj());
|
||||||
|
return {
|
||||||
|
{"diagnostics", items},
|
||||||
|
{"errorCount", errorCount()},
|
||||||
|
{"warningCount", warningCount()}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string toJson() const { return toJsonObj().dump(); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::vector<Diagnostic> diagnostics_;
|
||||||
|
|
||||||
|
int countLevel(DiagLevel level) const {
|
||||||
|
int n = 0;
|
||||||
|
for (const auto& d : diagnostics_)
|
||||||
|
if (d.level == level) ++n;
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // SAQUT_DIAGNOSTIC_ENGINE
|
||||||
|
|
@ -0,0 +1,150 @@
|
||||||
|
// ============================================================================
|
||||||
|
// saQut IR — Instruction (Tek Talimat)
|
||||||
|
//
|
||||||
|
// Sanal makine bu talimatlara bakarak ne yapacağını anlar.
|
||||||
|
// Her talimatın bir "opcode"u (ne iş yapacağı) ve birkaç operandı vardır.
|
||||||
|
// Operandlar ya slot numarasıdır (fonksiyonun yerel değişken/geçici depoları)
|
||||||
|
// ya da doğrudan bir sayı/isim değeridir.
|
||||||
|
//
|
||||||
|
// SLOT NEDİR?
|
||||||
|
// Her fonksiyon çağrısı kendi "frame"ini açar.
|
||||||
|
// Frame içinde numaralı kutucuklar vardır: slot[0], slot[1], ...
|
||||||
|
// Parametreler slot 0'dan başlar. Sonrasında lokal değişkenler
|
||||||
|
// ve hesaplama sırasında oluşan geçici değerler gelir.
|
||||||
|
// "slots[5] = 42" demek "5 numaralı kutucuğa 42 değerini koy" demektir.
|
||||||
|
//
|
||||||
|
// HANGİ OPCODE HANGİ ALANI KULLANIR?
|
||||||
|
// LOAD_CONST : dest, intValue
|
||||||
|
// LOAD_SLOT : dest, src
|
||||||
|
// ADD/SUB/... : dest, left, right
|
||||||
|
// LESS/LEQ/... : dest, left, right (sonuç: 1=doğru, 0=yanlış)
|
||||||
|
// JMP : jumpTarget
|
||||||
|
// JIF_FALSE : cond, jumpTarget
|
||||||
|
// CALL : dest, functionName, argSlots
|
||||||
|
// RETURN : src
|
||||||
|
// CALLHOST : functionName, argSlots
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
#ifndef SAQUT_IR_INSTRUCTION
|
||||||
|
#define SAQUT_IR_INSTRUCTION
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// Opcode — Sanal Makinenin Anlayacağı İşlem Kodları
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
enum class Opcode {
|
||||||
|
|
||||||
|
// --- Değer yükleme ---
|
||||||
|
LOAD_CONST, // slots[dest] = intValue (tam sayı sabitini slota yükle)
|
||||||
|
// Örnek: LOAD_CONST dest=3 val=10 → slot[3] = 10
|
||||||
|
|
||||||
|
LOAD_STRING, // slots[dest] = stringValue (metin sabitini slota yükle)
|
||||||
|
// Örnek: LOAD_STRING dest=2 val="Merhaba" → slot[2] = "Merhaba"
|
||||||
|
|
||||||
|
LOAD_SLOT, // slots[dest] = slots[src]
|
||||||
|
// Bir slotun değerini başka bir slota kopyalar.
|
||||||
|
// Atama işlemlerinde (x = y) kullanılır.
|
||||||
|
|
||||||
|
// --- Aritmetik (tümü: slots[dest] = slots[left] OP slots[right]) ---
|
||||||
|
ADD,
|
||||||
|
SUB,
|
||||||
|
MUL,
|
||||||
|
DIV, // UYARI: sıfıra bölme → runtime_error fırlatılır
|
||||||
|
MOD,
|
||||||
|
|
||||||
|
// --- Karşılaştırma (sonuç: 1 = doğru, 0 = yanlış) ---
|
||||||
|
LESS, // slots[left] < slots[right]
|
||||||
|
LESS_EQUAL, // slots[left] <= slots[right]
|
||||||
|
GREATER, // slots[left] > slots[right]
|
||||||
|
GREATER_EQUAL, // slots[left] >= slots[right]
|
||||||
|
EQUAL_EQUAL, // slots[left] == slots[right]
|
||||||
|
NOT_EQUAL, // slots[left] != slots[right]
|
||||||
|
|
||||||
|
// --- Kontrol akışı ---
|
||||||
|
JMP, // Koşulsuz atlama: ip = jumpTarget
|
||||||
|
JIF_FALSE, // Koşullu atlama: slots[cond] == 0 ise ip = jumpTarget
|
||||||
|
|
||||||
|
// --- Fonksiyon çağrısı ---
|
||||||
|
CALL, // Başka bir saQut fonksiyonunu çağır.
|
||||||
|
// Yeni frame açılır, argümanlar parametre slotlarına kopyalanır.
|
||||||
|
// Fonksiyon RETURN ile bitince sonuç slots[dest]'e yazılır.
|
||||||
|
|
||||||
|
RETURN, // Bu frame'i kapat, slots[src]'yi caller'a ilet.
|
||||||
|
|
||||||
|
// --- Dış dünya (FFI — Foreign Function Interface) ---
|
||||||
|
CALLHOST, // Host (C++) fonksiyonunu çağır. Şu an sadece "print" destekli.
|
||||||
|
// Dönüş değeri yok; sadece yan etki (stdout'a yazmak gibi).
|
||||||
|
};
|
||||||
|
|
||||||
|
// Hata ayıklama ve IR dump için okunabilir isim
|
||||||
|
inline const char* opcodeName(Opcode op) {
|
||||||
|
switch (op) {
|
||||||
|
case Opcode::LOAD_CONST: return "LOAD_CONST";
|
||||||
|
case Opcode::LOAD_STRING: return "LOAD_STRING";
|
||||||
|
case Opcode::LOAD_SLOT: return "LOAD_SLOT";
|
||||||
|
case Opcode::ADD: return "ADD";
|
||||||
|
case Opcode::SUB: return "SUB";
|
||||||
|
case Opcode::MUL: return "MUL";
|
||||||
|
case Opcode::DIV: return "DIV";
|
||||||
|
case Opcode::MOD: return "MOD";
|
||||||
|
case Opcode::LESS: return "LESS";
|
||||||
|
case Opcode::LESS_EQUAL: return "LESS_EQUAL";
|
||||||
|
case Opcode::GREATER: return "GREATER";
|
||||||
|
case Opcode::GREATER_EQUAL: return "GREATER_EQUAL";
|
||||||
|
case Opcode::EQUAL_EQUAL: return "EQUAL_EQUAL";
|
||||||
|
case Opcode::NOT_EQUAL: return "NOT_EQUAL";
|
||||||
|
case Opcode::JMP: return "JMP";
|
||||||
|
case Opcode::JIF_FALSE: return "JIF_FALSE";
|
||||||
|
case Opcode::CALL: return "CALL";
|
||||||
|
case Opcode::RETURN: return "RETURN";
|
||||||
|
case Opcode::CALLHOST: return "CALLHOST";
|
||||||
|
}
|
||||||
|
return "UNKNOWN";
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// Instruction — Tek bir IR talimatı
|
||||||
|
//
|
||||||
|
// Okunabilirlik öncelikli bir tasarım: her talimat TÜM alanları içerir,
|
||||||
|
// kullanılmayanlar varsayılan değerde (-1 veya boş) kalır.
|
||||||
|
// Bu yaklaşım bellek israfeder ama her talimatın hangi veriyle çalıştığı
|
||||||
|
// açıkça görünür — karmaşık union/variant yapısı gerekmez.
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
struct Instruction {
|
||||||
|
Opcode opcode;
|
||||||
|
|
||||||
|
// Hedef slot — sonucun yazılacağı yer (LOAD_CONST, ADD, CALL vb.)
|
||||||
|
int dest = -1;
|
||||||
|
|
||||||
|
// Kaynak slot — kopyalama veya döndürme için (LOAD_SLOT, RETURN)
|
||||||
|
int src = -1;
|
||||||
|
|
||||||
|
// Aritmetik/karşılaştırma operandları
|
||||||
|
int left = -1;
|
||||||
|
int right = -1;
|
||||||
|
|
||||||
|
// LOAD_CONST için yüklenecek tam sayı sabiti
|
||||||
|
int intValue = 0;
|
||||||
|
|
||||||
|
// LOAD_STRING için yüklenecek metin sabiti (tırnak işaretleri olmadan)
|
||||||
|
std::string stringValue;
|
||||||
|
|
||||||
|
// JMP / JIF_FALSE için hedef instruction indeksi
|
||||||
|
// Üretim sırasında bilinmiyorsa -1 bırakılır, sonradan doldurulur (backpatch).
|
||||||
|
int jumpTarget = -1;
|
||||||
|
|
||||||
|
// JIF_FALSE için kontrol edilecek koşul slotu
|
||||||
|
int cond = -1;
|
||||||
|
|
||||||
|
// CALL / CALLHOST için çağrılacak fonksiyonun adı
|
||||||
|
std::string functionName;
|
||||||
|
|
||||||
|
// CALL / CALLHOST için argüman slot indeksleri (sırayla)
|
||||||
|
std::vector<int> argSlots;
|
||||||
|
|
||||||
|
explicit Instruction(Opcode op) : opcode(op) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // SAQUT_IR_INSTRUCTION
|
||||||
|
|
@ -0,0 +1,123 @@
|
||||||
|
#include "ir/ir_function.hpp"
|
||||||
|
#include <iomanip>
|
||||||
|
#include <iostream>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
// Yardımcılar
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
// Slot adını kısa göster: s0, s1, ...
|
||||||
|
static std::string slot(int s) {
|
||||||
|
if (s == -1) return "?";
|
||||||
|
return "s" + std::to_string(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
// İkili op sembolü: ADD → "+"
|
||||||
|
static const char* opSymbol(Opcode op) {
|
||||||
|
switch (op) {
|
||||||
|
case Opcode::ADD: return "+";
|
||||||
|
case Opcode::SUB: return "-";
|
||||||
|
case Opcode::MUL: return "*";
|
||||||
|
case Opcode::DIV: return "/";
|
||||||
|
case Opcode::MOD: return "%";
|
||||||
|
case Opcode::LESS: return "<";
|
||||||
|
case Opcode::LESS_EQUAL: return "<=";
|
||||||
|
case Opcode::GREATER: return ">";
|
||||||
|
case Opcode::GREATER_EQUAL: return ">=";
|
||||||
|
case Opcode::EQUAL_EQUAL: return "==";
|
||||||
|
case Opcode::NOT_EQUAL: return "!=";
|
||||||
|
default: return "?";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool isBinaryOp(Opcode op) {
|
||||||
|
switch (op) {
|
||||||
|
case Opcode::ADD: case Opcode::SUB: case Opcode::MUL:
|
||||||
|
case Opcode::DIV: case Opcode::MOD:
|
||||||
|
case Opcode::LESS: case Opcode::LESS_EQUAL:
|
||||||
|
case Opcode::GREATER: case Opcode::GREATER_EQUAL:
|
||||||
|
case Opcode::EQUAL_EQUAL: case Opcode::NOT_EQUAL:
|
||||||
|
return true;
|
||||||
|
default: return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
// IRFunction::dump
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
void IRFunction::dump() const {
|
||||||
|
// Başlık: fonksiyon adı + slot bilgisi
|
||||||
|
std::string header = " " + name + "()";
|
||||||
|
if (paramCount > 0) {
|
||||||
|
header = " " + name + "(";
|
||||||
|
for (int i = 0; i < paramCount; i++) {
|
||||||
|
if (i) header += ", ";
|
||||||
|
header += "s" + std::to_string(i);
|
||||||
|
}
|
||||||
|
header += ")";
|
||||||
|
}
|
||||||
|
header += " [" + std::to_string(slotCount) + " slot]";
|
||||||
|
|
||||||
|
// Başlık: NAME=fibonacci PARAMS=1 SLOTS=10
|
||||||
|
std::cout << "NAME=" << name
|
||||||
|
<< " PARAMS=" << paramCount
|
||||||
|
<< " SLOTS=" << slotCount
|
||||||
|
<< "\n";
|
||||||
|
|
||||||
|
// Talimatlar
|
||||||
|
for (int i = 0; i < (int)instructions.size(); i++) {
|
||||||
|
const Instruction& ins = instructions[i];
|
||||||
|
|
||||||
|
// Satır numarası
|
||||||
|
std::cout << " " << std::setw(3) << std::right << i << " ";
|
||||||
|
|
||||||
|
// Opcode sütunu (12 karakter genişlik)
|
||||||
|
std::cout << std::left << std::setw(12) << opcodeName(ins.opcode);
|
||||||
|
|
||||||
|
// Operandlar — opcode'a göre farklı format
|
||||||
|
if (ins.opcode == Opcode::LOAD_CONST) {
|
||||||
|
std::cout << slot(ins.dest) << " = " << ins.intValue;
|
||||||
|
|
||||||
|
} else if (ins.opcode == Opcode::LOAD_STRING) {
|
||||||
|
std::cout << slot(ins.dest) << " = \"" << ins.stringValue << "\"";
|
||||||
|
|
||||||
|
} else if (ins.opcode == Opcode::LOAD_SLOT) {
|
||||||
|
std::cout << slot(ins.dest) << " = " << slot(ins.src);
|
||||||
|
|
||||||
|
} else if (isBinaryOp(ins.opcode)) {
|
||||||
|
std::cout << slot(ins.dest) << " = "
|
||||||
|
<< slot(ins.left) << " " << opSymbol(ins.opcode)
|
||||||
|
<< " " << slot(ins.right);
|
||||||
|
|
||||||
|
} else if (ins.opcode == Opcode::JMP) {
|
||||||
|
std::cout << "→ " << ins.jumpTarget;
|
||||||
|
|
||||||
|
} else if (ins.opcode == Opcode::JIF_FALSE) {
|
||||||
|
std::cout << "!" << slot(ins.cond) << " → " << ins.jumpTarget;
|
||||||
|
|
||||||
|
} else if (ins.opcode == Opcode::CALL) {
|
||||||
|
std::cout << slot(ins.dest) << " = " << ins.functionName << "(";
|
||||||
|
for (int j = 0; j < (int)ins.argSlots.size(); j++) {
|
||||||
|
if (j) std::cout << ", ";
|
||||||
|
std::cout << slot(ins.argSlots[j]);
|
||||||
|
}
|
||||||
|
std::cout << ")";
|
||||||
|
|
||||||
|
} else if (ins.opcode == Opcode::CALLHOST) {
|
||||||
|
std::cout << ins.functionName << "(";
|
||||||
|
for (int j = 0; j < (int)ins.argSlots.size(); j++) {
|
||||||
|
if (j) std::cout << ", ";
|
||||||
|
std::cout << slot(ins.argSlots[j]);
|
||||||
|
}
|
||||||
|
std::cout << ")";
|
||||||
|
|
||||||
|
} else if (ins.opcode == Opcode::RETURN) {
|
||||||
|
std::cout << slot(ins.src);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << "\n";
|
||||||
|
}
|
||||||
|
std::cout << "\n";
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,37 @@
|
||||||
|
// ============================================================================
|
||||||
|
// saQut IR — IRFunction (Tek Fonksiyonun IR Karşılığı)
|
||||||
|
//
|
||||||
|
// Bir IRFunction, kaynak koddaki tek bir fonksiyonun "pişmiş" halidir.
|
||||||
|
// IRGenerator bu yapıyı doldurur, Interpreter bu yapıyı çalıştırır.
|
||||||
|
//
|
||||||
|
// SLOT DÜZENI:
|
||||||
|
// slot[0 .. paramCount-1] → parametreler (soldan sağa)
|
||||||
|
// slot[paramCount ..] → lokal değişkenler ve geçici sonuçlar
|
||||||
|
// slotCount → toplam kaç slot lazım (frame boyutu)
|
||||||
|
//
|
||||||
|
// Örnek — fibonacci(int n):
|
||||||
|
// paramCount = 1 → slot[0] = n
|
||||||
|
// slotCount = 11 → slot[0..10] (0'ı parametre, 1-10 hesaplamalar)
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
#ifndef SAQUT_IR_FUNCTION
|
||||||
|
#define SAQUT_IR_FUNCTION
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include "ir/instruction.hpp"
|
||||||
|
|
||||||
|
struct IRFunction {
|
||||||
|
std::string name; // kaynak koddaki fonksiyon adı
|
||||||
|
int paramCount; // kaç parametresi var
|
||||||
|
int slotCount; // frame boyutu (üretim sonunda doldurulur)
|
||||||
|
std::vector<Instruction> instructions; // bu fonksiyonun talimat listesi
|
||||||
|
|
||||||
|
IRFunction(std::string name, int paramCount)
|
||||||
|
: name(std::move(name)), paramCount(paramCount), slotCount(0) {}
|
||||||
|
|
||||||
|
// Okunabilir IR dump — "saqut run" hata ayıklaması veya inceleme için
|
||||||
|
void dump() const;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // SAQUT_IR_FUNCTION
|
||||||
|
|
@ -0,0 +1,571 @@
|
||||||
|
#include "ir/ir_generator.hpp"
|
||||||
|
#include "tokenizer/token.hpp"
|
||||||
|
#include "parser/nodes/program.hpp"
|
||||||
|
#include "parser/nodes/declarations.hpp"
|
||||||
|
#include "parser/nodes/statements.hpp"
|
||||||
|
#include "parser/nodes/expressions.hpp"
|
||||||
|
#include "parser/nodes/binary_expr.hpp"
|
||||||
|
#include "parser/nodes/identifier.hpp"
|
||||||
|
#include "parser/nodes/literal.hpp"
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
// generate — Ana giriş noktası
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
IRProgram IRGenerator::generate(ASTNode* programNode, SymbolTable& /*symbolTable*/) {
|
||||||
|
IRProgram program;
|
||||||
|
|
||||||
|
// ProgramNode'un her çocuğunu gez.
|
||||||
|
// Bizi ilgilendiren: FunctionDecl. StructDecl/GlobalVar → TODO.
|
||||||
|
for (ASTNode* child : programNode->getChildren()) {
|
||||||
|
if (child->kind == ASTKind::FunctionDecl) {
|
||||||
|
// Her fonksiyon üretimi için sıfırla
|
||||||
|
nameToSlot_.clear();
|
||||||
|
nextSlot_ = 0;
|
||||||
|
|
||||||
|
// IRFunction oluştur, currentFunction_ olarak işaretle
|
||||||
|
auto* fnDecl = (FunctionDeclNode*)child;
|
||||||
|
IRFunction irFn(fnDecl->name, (int)fnDecl->params.size());
|
||||||
|
program.addFunction(std::move(irFn));
|
||||||
|
|
||||||
|
// addFunction std::move yaptığı için pointer'ı haritadan alalım
|
||||||
|
currentFunction_ = program.findFunction(fnDecl->name);
|
||||||
|
|
||||||
|
generateFunction(child);
|
||||||
|
|
||||||
|
// Fonksiyon bitti — toplam slot sayısını kaydet
|
||||||
|
currentFunction_->slotCount = nextSlot_;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return program;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
// generateFunction — Tek bir fonksiyonu IR'a çevirir
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
void IRGenerator::generateFunction(ASTNode* functionDeclNode) {
|
||||||
|
auto* fn = (FunctionDeclNode*)functionDeclNode;
|
||||||
|
|
||||||
|
// Parametreler slot 0, 1, 2, ... sırasıyla alır.
|
||||||
|
// Interpreter, CALL sırasında bu slotlara argümanları kopyalar.
|
||||||
|
for (auto* param : fn->params) {
|
||||||
|
int slot = freshSlot();
|
||||||
|
registerVariable(param->name, slot);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fonksiyon gövdesi — children[0] her zaman BlockNode
|
||||||
|
auto& children = fn->getChildren();
|
||||||
|
if (!children.empty()) {
|
||||||
|
generateStatement(children[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
// generateStatement — Deyim türlerine göre talimat üret
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
void IRGenerator::generateStatement(ASTNode* node) {
|
||||||
|
if (!node) return;
|
||||||
|
|
||||||
|
switch (node->kind) {
|
||||||
|
|
||||||
|
// ── Blok: içindeki her deyimi sırayla üret ───────────────────────────
|
||||||
|
case ASTKind::Block: {
|
||||||
|
for (ASTNode* child : node->getChildren()) {
|
||||||
|
generateStatement(child);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Değişken bildirimi: int x = <ifade> ──────────────────────────────
|
||||||
|
case ASTKind::VariableDecl: {
|
||||||
|
auto* vd = (VariableDeclNode*)node;
|
||||||
|
|
||||||
|
// Bu değişken için yeni bir slot ayır
|
||||||
|
int varSlot = freshSlot();
|
||||||
|
registerVariable(vd->name, varSlot);
|
||||||
|
|
||||||
|
if (vd->initExpr) {
|
||||||
|
// Başlatma ifadesini üret, sonucu bir slotta al
|
||||||
|
int initSlot = generateExpression(vd->initExpr);
|
||||||
|
|
||||||
|
if (initSlot != varSlot) {
|
||||||
|
// Sonuç başka bir slotta, değişkenin slotuna kopyala
|
||||||
|
emitLoadSlot(varSlot, initSlot);
|
||||||
|
}
|
||||||
|
// initSlot == varSlot: LOAD_CONST doğrudan varSlot'a yazıldı, kopya gerekmez
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sibling VariableDecl'ler: int a, b; → children'da diğer VariableDecl'ler
|
||||||
|
for (ASTNode* sib : node->getChildren()) {
|
||||||
|
if (sib->kind == ASTKind::VariableDecl) {
|
||||||
|
generateStatement(sib);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── return <ifade> ───────────────────────────────────────────────────
|
||||||
|
case ASTKind::ReturnStatement: {
|
||||||
|
auto* rs = (ReturnStatementNode*)node;
|
||||||
|
int returnSlot = 0; // varsayılan: slot[0] (void fonksiyon / boş return)
|
||||||
|
|
||||||
|
if (rs->value) {
|
||||||
|
returnSlot = generateExpression(rs->value);
|
||||||
|
}
|
||||||
|
emitReturn(returnSlot);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── if (koşul) { ... } [else { ... }] ───────────────────────────────
|
||||||
|
case ASTKind::IfStatement: {
|
||||||
|
auto* ifn = (IfStatementNode*)node;
|
||||||
|
|
||||||
|
// Koşulu hesapla
|
||||||
|
int condSlot = generateExpression(ifn->condition);
|
||||||
|
|
||||||
|
// "Koşul yanlışsa atla" → hedef henüz bilinmiyor, backpatch bekliyor
|
||||||
|
int jumpToElse = emitJumpIfFalse(condSlot);
|
||||||
|
|
||||||
|
// Then bloğu
|
||||||
|
if (ifn->thenBranch) generateStatement(ifn->thenBranch);
|
||||||
|
|
||||||
|
if (ifn->elseBranch) {
|
||||||
|
// Then bitti, else'i atla (then içinde çalışanlar else'e girmemeli)
|
||||||
|
int jumpOverElse = emitJumpUnconditional(-1);
|
||||||
|
// Şimdi else'in başlangıç konumunu biliyoruz → jumpToElse'i doldur
|
||||||
|
patchJump(jumpToElse);
|
||||||
|
generateStatement(ifn->elseBranch);
|
||||||
|
// Else bitti → jumpOverElse'i doldur
|
||||||
|
patchJump(jumpOverElse);
|
||||||
|
} else {
|
||||||
|
// Else yok → jumpToElse doğrudan if sonrasına atlıyor
|
||||||
|
patchJump(jumpToElse);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── while (koşul) { gövde } ──────────────────────────────────────────
|
||||||
|
case ASTKind::WhileStatement: {
|
||||||
|
auto* ws = (WhileStatementNode*)node;
|
||||||
|
|
||||||
|
// Döngü başının konumu — geri-jump buraya gelecek
|
||||||
|
int loopStart = currentInstrIndex();
|
||||||
|
|
||||||
|
int condSlot = generateExpression(ws->condition);
|
||||||
|
int exitJump = emitJumpIfFalse(condSlot); // ileri, backpatch bekliyor
|
||||||
|
|
||||||
|
if (ws->body) generateStatement(ws->body);
|
||||||
|
|
||||||
|
// Geri-jump: hedef zaten biliniyor (loopStart)
|
||||||
|
emitJumpUnconditional(loopStart);
|
||||||
|
|
||||||
|
// Döngü çıkış noktası → exitJump'ı doldur
|
||||||
|
patchJump(exitJump);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── for (init; koşul; güncelleme) { gövde } ─────────────────────────
|
||||||
|
//
|
||||||
|
// Üretilen IR yapısı:
|
||||||
|
// [init]
|
||||||
|
// LOOP_START:
|
||||||
|
// [koşul] → condSlot
|
||||||
|
// JIF_FALSE condSlot → LOOP_END (ileri-jump, backpatch)
|
||||||
|
// [gövde]
|
||||||
|
// [güncelleme]
|
||||||
|
// JMP → LOOP_START (geri-jump, hedef biliniyor)
|
||||||
|
// LOOP_END:
|
||||||
|
// ─────────────────────────────────────────────────────────────────────
|
||||||
|
case ASTKind::ForStatement: {
|
||||||
|
auto* fs = (ForStatementNode*)node;
|
||||||
|
|
||||||
|
// Init: genellikle "int i = 0" gibi bir VariableDecl
|
||||||
|
if (fs->init) generateStatement(fs->init);
|
||||||
|
|
||||||
|
// Döngü başı konumu — geri-jump'ın hedefi
|
||||||
|
int loopStart = currentInstrIndex();
|
||||||
|
|
||||||
|
// Koşul
|
||||||
|
int condSlot = fs->condition ? generateExpression(fs->condition) : -1;
|
||||||
|
int exitJump = (condSlot != -1) ? emitJumpIfFalse(condSlot) : -1;
|
||||||
|
|
||||||
|
// Gövde
|
||||||
|
if (fs->body) generateStatement(fs->body);
|
||||||
|
|
||||||
|
// Güncelleme (ör: i = i + 1) — ifade deyimi, sonuç önemsiz
|
||||||
|
if (fs->update) generateExpression(fs->update);
|
||||||
|
|
||||||
|
// Geri-jump: hedef loopStart, zaten biliniyor
|
||||||
|
emitJumpUnconditional(loopStart);
|
||||||
|
|
||||||
|
// Döngü çıkışı → exitJump'ı doldur
|
||||||
|
if (exitJump != -1) patchJump(exitJump);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── do { gövde } while (koşul) ───────────────────────────────────────
|
||||||
|
case ASTKind::DoWhileStatement: {
|
||||||
|
auto* dw = (DoWhileStatementNode*)node;
|
||||||
|
int loopStart = currentInstrIndex();
|
||||||
|
|
||||||
|
if (dw->body) generateStatement(dw->body);
|
||||||
|
|
||||||
|
int condSlot = generateExpression(dw->condition);
|
||||||
|
// Koşul doğruysa geri atla (1 = doğru → atla; 0 = yanlış → devam)
|
||||||
|
// JIF_FALSE koşul yanlışsa atlar; biz doğruysa atlamak istiyoruz.
|
||||||
|
// Bu yüzden JIF_FALSE yerine "doğruysa atla" mantığı lazım.
|
||||||
|
// Basit çözüm: koşulun tersini al (0→1, diğer→0) ve JIF_FALSE kullan.
|
||||||
|
// NOT: saQut'ta "!" operatörü yok henüz; NOT talimatı eklenebilir.
|
||||||
|
// Şimdilik: koşul slotuna bak, sıfır değilse geri atla.
|
||||||
|
// TODO(vm-genişletme): JIF_TRUE talimatı ekle
|
||||||
|
// Geçici çözüm: sabit 1 ile karşılaştır (condSlot != 0 → geri)
|
||||||
|
int oneSlot = freshSlot();
|
||||||
|
emitLoadConst(oneSlot, 1);
|
||||||
|
int eqSlot = freshSlot();
|
||||||
|
emitBinaryOp(Opcode::EQUAL_EQUAL, eqSlot, condSlot, oneSlot);
|
||||||
|
int skipJump = emitJumpIfFalse(eqSlot); // koşul yanlışsa döngüden çık
|
||||||
|
emitJumpUnconditional(loopStart); // geri atla
|
||||||
|
patchJump(skipJump);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── İfade deyimi: bir ifadeyi değerlendirip sonucu at ────────────────
|
||||||
|
// Örnek: print(x) çağrısı, veya x = 5 ataması
|
||||||
|
case ASTKind::ExpressionStatement: {
|
||||||
|
auto* es = (ExpressionStatementNode*)node;
|
||||||
|
if (es->expression) {
|
||||||
|
generateExpression(es->expression); // sonucu kullanmıyoruz
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case ASTKind::BreakStatement:
|
||||||
|
case ASTKind::ContinueStatement:
|
||||||
|
// TODO(vm-genişletme): break/continue için JMP + label mekanizması gerekir
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
// generateExpression — İfadeyi IR'a çevirir, sonucu içeren slot'u döndürür
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
int IRGenerator::generateExpression(ASTNode* node) {
|
||||||
|
if (!node) return 0;
|
||||||
|
|
||||||
|
switch (node->kind) {
|
||||||
|
|
||||||
|
// ── Sabit değer: 42, 3.14, true ... ──────────────────────────────────
|
||||||
|
case ASTKind::Literal: {
|
||||||
|
auto* lit = (LiteralNode*)node;
|
||||||
|
int slot = freshSlot();
|
||||||
|
|
||||||
|
switch (lit->literalType) {
|
||||||
|
case LiteralType::INTEGER: {
|
||||||
|
int value = 0;
|
||||||
|
if (lit->parserToken.token)
|
||||||
|
value = std::stoi(lit->parserToken.token->token);
|
||||||
|
emitLoadConst(slot, value);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case LiteralType::BOOLEAN: {
|
||||||
|
int value = (lit->parserToken.token &&
|
||||||
|
lit->parserToken.token->token == "true") ? 1 : 0;
|
||||||
|
emitLoadConst(slot, value);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case LiteralType::STRING: {
|
||||||
|
// StringToken::context tırnak işaretleri olmadan içeriği tutar
|
||||||
|
std::string content;
|
||||||
|
if (auto* st = dynamic_cast<StringToken*>(lit->lexerToken))
|
||||||
|
content = st->context;
|
||||||
|
else if (lit->parserToken.token) {
|
||||||
|
// Fallback: token'ın başındaki ve sonundaki " işaretlerini sıyır
|
||||||
|
std::string raw = lit->parserToken.token->token;
|
||||||
|
if (raw.size() >= 2 && raw.front() == '"' && raw.back() == '"')
|
||||||
|
content = raw.substr(1, raw.size() - 2);
|
||||||
|
else
|
||||||
|
content = raw;
|
||||||
|
}
|
||||||
|
Instruction ins(Opcode::LOAD_STRING);
|
||||||
|
ins.dest = slot;
|
||||||
|
ins.stringValue = std::move(content);
|
||||||
|
currentFunction_->instructions.push_back(std::move(ins));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case LiteralType::FLOAT:
|
||||||
|
throw std::runtime_error(
|
||||||
|
"IR üretim hatası: float literal şu an VM tarafından desteklenmiyor. "
|
||||||
|
"Tam sayı kullanın veya float desteği eklenene kadar bekleyin.");
|
||||||
|
case LiteralType::BOŞ:
|
||||||
|
throw std::runtime_error(
|
||||||
|
"IR üretim hatası: null literal şu an VM tarafından desteklenmiyor.");
|
||||||
|
}
|
||||||
|
return slot;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Değişken ismi: n, first, second ... ──────────────────────────────
|
||||||
|
// Bu değişkenin değeri zaten bir slotta. O slotu döndür.
|
||||||
|
case ASTKind::Identifier: {
|
||||||
|
auto* id = (IdentifierNode*)node;
|
||||||
|
std::string name = id->parserToken.token ? id->parserToken.token->token : "";
|
||||||
|
|
||||||
|
// Önce builtin mi? (print gibi) — identifier olarak gelen builtin fonksiyon
|
||||||
|
// çağrıları CallExpression içinde yakalanıyor, burada sadece değişken kalır
|
||||||
|
return lookupVariable(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── İkili ifade: x + y, x = y, x < y ... ────────────────────────────
|
||||||
|
case ASTKind::BinaryExpression: {
|
||||||
|
auto* bin = (BinaryExpressionNode*)node;
|
||||||
|
|
||||||
|
// Atama operatörleri: x = expr, x += expr ...
|
||||||
|
// Sol taraf bir değişken, sağ taraf hesaplanır ve o değişkene yazılır.
|
||||||
|
if (bin->Operator == TokenType::EQUAL) {
|
||||||
|
// Sağ tarafı hesapla
|
||||||
|
int rhsSlot = generateExpression(bin->Right);
|
||||||
|
|
||||||
|
// Sol taraf değişkenin slotunu bul
|
||||||
|
auto* lhsId = (IdentifierNode*)bin->Left;
|
||||||
|
std::string varName = lhsId->parserToken.token->token;
|
||||||
|
int varSlot = lookupVariable(varName);
|
||||||
|
|
||||||
|
// Sonucu değişkenin slotuna kopyala
|
||||||
|
if (rhsSlot != varSlot) {
|
||||||
|
emitLoadSlot(varSlot, rhsSlot);
|
||||||
|
}
|
||||||
|
return varSlot;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Birleşik atama: += -= *= /=
|
||||||
|
// x += y ≡ x = x + y
|
||||||
|
if (bin->Operator == TokenType::PLUS_EQUAL ||
|
||||||
|
bin->Operator == TokenType::MINUS_EQUAL ||
|
||||||
|
bin->Operator == TokenType::STAR_EQUAL ||
|
||||||
|
bin->Operator == TokenType::SLASH_EQUAL) {
|
||||||
|
|
||||||
|
auto* lhsId = (IdentifierNode*)bin->Left;
|
||||||
|
std::string varName = lhsId->parserToken.token->token;
|
||||||
|
int varSlot = lookupVariable(varName);
|
||||||
|
int rhsSlot = generateExpression(bin->Right);
|
||||||
|
|
||||||
|
Opcode arithOp = Opcode::ADD;
|
||||||
|
if (bin->Operator == TokenType::MINUS_EQUAL) arithOp = Opcode::SUB;
|
||||||
|
else if (bin->Operator == TokenType::STAR_EQUAL) arithOp = Opcode::MUL;
|
||||||
|
else if (bin->Operator == TokenType::SLASH_EQUAL) arithOp = Opcode::DIV;
|
||||||
|
|
||||||
|
int resultSlot = freshSlot();
|
||||||
|
emitBinaryOp(arithOp, resultSlot, varSlot, rhsSlot);
|
||||||
|
emitLoadSlot(varSlot, resultSlot);
|
||||||
|
return varSlot;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unary prefix: Left = nullptr (ör: -x, !x)
|
||||||
|
if (!bin->Left) {
|
||||||
|
int operandSlot = generateExpression(bin->Right);
|
||||||
|
int resultSlot = freshSlot();
|
||||||
|
|
||||||
|
if (bin->Operator == TokenType::MINUS) {
|
||||||
|
// -x → 0 - x
|
||||||
|
int zeroSlot = freshSlot();
|
||||||
|
emitLoadConst(zeroSlot, 0);
|
||||||
|
emitBinaryOp(Opcode::SUB, resultSlot, zeroSlot, operandSlot);
|
||||||
|
} else {
|
||||||
|
// Diğer unary operatörler → TODO
|
||||||
|
emitLoadSlot(resultSlot, operandSlot);
|
||||||
|
}
|
||||||
|
return resultSlot;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Aritmetik operatörler
|
||||||
|
switch (bin->Operator) {
|
||||||
|
case TokenType::PLUS: return generateBinaryArithmetic(Opcode::ADD, bin->Left, bin->Right);
|
||||||
|
case TokenType::MINUS: return generateBinaryArithmetic(Opcode::SUB, bin->Left, bin->Right);
|
||||||
|
case TokenType::STAR: return generateBinaryArithmetic(Opcode::MUL, bin->Left, bin->Right);
|
||||||
|
case TokenType::SLASH: return generateBinaryArithmetic(Opcode::DIV, bin->Left, bin->Right);
|
||||||
|
case TokenType::PERCENT: return generateBinaryArithmetic(Opcode::MOD, bin->Left, bin->Right);
|
||||||
|
// Karşılaştırma operatörleri
|
||||||
|
case TokenType::LESS: return generateBinaryArithmetic(Opcode::LESS, bin->Left, bin->Right);
|
||||||
|
case TokenType::LESS_EQUAL: return generateBinaryArithmetic(Opcode::LESS_EQUAL, bin->Left, bin->Right);
|
||||||
|
case TokenType::GREATER: return generateBinaryArithmetic(Opcode::GREATER, bin->Left, bin->Right);
|
||||||
|
case TokenType::GREATER_EQUAL: return generateBinaryArithmetic(Opcode::GREATER_EQUAL, bin->Left, bin->Right);
|
||||||
|
case TokenType::EQUAL_EQUAL: return generateBinaryArithmetic(Opcode::EQUAL_EQUAL, bin->Left, bin->Right);
|
||||||
|
case TokenType::BANG_EQUAL: return generateBinaryArithmetic(Opcode::NOT_EQUAL, bin->Left, bin->Right);
|
||||||
|
default: {
|
||||||
|
// Bilinmeyen operatör — boş slot döndür
|
||||||
|
int slot = freshSlot();
|
||||||
|
emitLoadConst(slot, 0);
|
||||||
|
return slot;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Fonksiyon çağrısı: fibonacci(n-1), print(x) ... ─────────────────
|
||||||
|
case ASTKind::Call: {
|
||||||
|
auto* call = (CallExpressionNode*)node;
|
||||||
|
|
||||||
|
// Hangi fonksiyon çağrılıyor? Callee bir Identifier
|
||||||
|
std::string fnName;
|
||||||
|
bool isBuiltin = false;
|
||||||
|
|
||||||
|
if (call->callee && call->callee->kind == ASTKind::Identifier) {
|
||||||
|
auto* calleeId = (IdentifierNode*)call->callee;
|
||||||
|
if (calleeId->parserToken.token) {
|
||||||
|
fnName = calleeId->parserToken.token->token;
|
||||||
|
}
|
||||||
|
// Builtin kontrolü: resolvedSymbol->isBuiltin
|
||||||
|
if (calleeId->resolvedSymbol && calleeId->resolvedSymbol->isBuiltin) {
|
||||||
|
isBuiltin = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Her argümanı hesapla, sonuçların slot numaralarını topla
|
||||||
|
std::vector<int> argSlots;
|
||||||
|
for (ASTNode* arg : call->arguments) {
|
||||||
|
argSlots.push_back(generateExpression(arg));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isBuiltin) {
|
||||||
|
// CALLHOST: host (C++) fonksiyonu çağır (print gibi), dönüş değeri yok
|
||||||
|
Instruction ins(Opcode::CALLHOST);
|
||||||
|
ins.functionName = fnName;
|
||||||
|
ins.argSlots = argSlots;
|
||||||
|
currentFunction_->instructions.push_back(std::move(ins));
|
||||||
|
return -1; // Dönüş değeri yok
|
||||||
|
} else {
|
||||||
|
// CALL: saQut fonksiyonu çağır, sonucu yeni slota yaz
|
||||||
|
int destSlot = freshSlot();
|
||||||
|
Instruction ins(Opcode::CALL);
|
||||||
|
ins.dest = destSlot;
|
||||||
|
ins.functionName = fnName;
|
||||||
|
ins.argSlots = argSlots;
|
||||||
|
currentFunction_->instructions.push_back(std::move(ins));
|
||||||
|
return destSlot;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Postfix: i++, i-- ────────────────────────────────────────────────
|
||||||
|
case ASTKind::Postfix: {
|
||||||
|
auto* pf = (PostfixNode*)node;
|
||||||
|
// Şu anki değeri döndür, sonra artır/azalt
|
||||||
|
int operandSlot = generateExpression(pf->operand);
|
||||||
|
int resultSlot = freshSlot(); // dönüş değeri (artırmadan önceki)
|
||||||
|
emitLoadSlot(resultSlot, operandSlot);
|
||||||
|
|
||||||
|
int oneSlot = freshSlot();
|
||||||
|
emitLoadConst(oneSlot, 1);
|
||||||
|
int newSlot = freshSlot();
|
||||||
|
|
||||||
|
if (pf->Operator == TokenType::PLUS_PLUS) {
|
||||||
|
emitBinaryOp(Opcode::ADD, newSlot, operandSlot, oneSlot);
|
||||||
|
} else {
|
||||||
|
emitBinaryOp(Opcode::SUB, newSlot, operandSlot, oneSlot);
|
||||||
|
}
|
||||||
|
emitLoadSlot(operandSlot, newSlot); // orijinal değişkeni güncelle
|
||||||
|
return resultSlot; // artırmadan önceki değer
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
// Bilinmeyen ifade türü
|
||||||
|
return freshSlot(); // boş slot (0 değeriyle)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
// generateBinaryArithmetic — İkili op için sol+sağ üret, talimat ekle
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
int IRGenerator::generateBinaryArithmetic(Opcode opcode, ASTNode* leftNode, ASTNode* rightNode) {
|
||||||
|
int leftSlot = generateExpression(leftNode);
|
||||||
|
int rightSlot = generateExpression(rightNode);
|
||||||
|
int destSlot = freshSlot();
|
||||||
|
emitBinaryOp(opcode, destSlot, leftSlot, rightSlot);
|
||||||
|
return destSlot;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
// Slot yönetimi
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
int IRGenerator::freshSlot() {
|
||||||
|
return nextSlot_++;
|
||||||
|
}
|
||||||
|
|
||||||
|
void IRGenerator::registerVariable(const std::string& name, int slot) {
|
||||||
|
nameToSlot_[name] = slot;
|
||||||
|
}
|
||||||
|
|
||||||
|
int IRGenerator::lookupVariable(const std::string& name) {
|
||||||
|
auto it = nameToSlot_.find(name);
|
||||||
|
if (it == nameToSlot_.end()) {
|
||||||
|
// Bu noktaya normalde gelinmemeli; sembol toplayıcı E001 üretmiş olur.
|
||||||
|
// Yine de çökmemek için 0 döndür.
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return it->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
// Talimat yazma yardımcıları
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
void IRGenerator::emitLoadConst(int destSlot, int value) {
|
||||||
|
Instruction ins(Opcode::LOAD_CONST);
|
||||||
|
ins.dest = destSlot;
|
||||||
|
ins.intValue = value;
|
||||||
|
currentFunction_->instructions.push_back(std::move(ins));
|
||||||
|
}
|
||||||
|
|
||||||
|
void IRGenerator::emitLoadSlot(int destSlot, int srcSlot) {
|
||||||
|
Instruction ins(Opcode::LOAD_SLOT);
|
||||||
|
ins.dest = destSlot;
|
||||||
|
ins.src = srcSlot;
|
||||||
|
currentFunction_->instructions.push_back(std::move(ins));
|
||||||
|
}
|
||||||
|
|
||||||
|
void IRGenerator::emitBinaryOp(Opcode op, int destSlot, int leftSlot, int rightSlot) {
|
||||||
|
Instruction ins(op);
|
||||||
|
ins.dest = destSlot;
|
||||||
|
ins.left = leftSlot;
|
||||||
|
ins.right = rightSlot;
|
||||||
|
currentFunction_->instructions.push_back(std::move(ins));
|
||||||
|
}
|
||||||
|
|
||||||
|
void IRGenerator::emitReturn(int srcSlot) {
|
||||||
|
Instruction ins(Opcode::RETURN);
|
||||||
|
ins.src = srcSlot;
|
||||||
|
currentFunction_->instructions.push_back(std::move(ins));
|
||||||
|
}
|
||||||
|
|
||||||
|
int IRGenerator::emitJumpUnconditional(int targetInstrIndex) {
|
||||||
|
Instruction ins(Opcode::JMP);
|
||||||
|
ins.jumpTarget = targetInstrIndex;
|
||||||
|
currentFunction_->instructions.push_back(std::move(ins));
|
||||||
|
return (int)currentFunction_->instructions.size() - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int IRGenerator::emitJumpIfFalse(int condSlot) {
|
||||||
|
Instruction ins(Opcode::JIF_FALSE);
|
||||||
|
ins.cond = condSlot;
|
||||||
|
ins.jumpTarget = -1; // henüz bilinmiyor — patchJump() bekliyor
|
||||||
|
currentFunction_->instructions.push_back(std::move(ins));
|
||||||
|
// Bu instruction'ın indeksini döndür (backpatch için)
|
||||||
|
return (int)currentFunction_->instructions.size() - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void IRGenerator::patchJump(int instrIndex) {
|
||||||
|
// instrIndex'teki JMP veya JIF_FALSE'un hedefini şu anki konuma doldur
|
||||||
|
currentFunction_->instructions[instrIndex].jumpTarget = currentInstrIndex();
|
||||||
|
}
|
||||||
|
|
||||||
|
int IRGenerator::currentInstrIndex() const {
|
||||||
|
return (int)currentFunction_->instructions.size();
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,84 @@
|
||||||
|
// ============================================================================
|
||||||
|
// saQut IR — IRGenerator (AST → IR Dönüşümü)
|
||||||
|
//
|
||||||
|
// AST'yi (parse edilmiş kaynak kodu) Instruction listelerine çevirir.
|
||||||
|
// Her fonksiyon için bir IRFunction üretir, hepsini IRProgram'a toplar.
|
||||||
|
//
|
||||||
|
// SLOT ATAMA STRATEJİSİ:
|
||||||
|
// - Her fonksiyon üretiminde nextSlot_ sıfırdan başlar.
|
||||||
|
// - Parametreler 0, 1, 2, ... slotlarına sırayla atanır.
|
||||||
|
// - Sonraki her değişken veya geçici sonuç freshSlot() ile yeni slot alır.
|
||||||
|
// - Slotlar asla geri verilmez (basitlik öncelikli).
|
||||||
|
// - Fonksiyon bitince nextSlot_ = slotCount.
|
||||||
|
//
|
||||||
|
// SINIRLAMALAR (fibonacci için yeterli, genel dil için TODO):
|
||||||
|
// - Aynı isimli iki değişken farklı iç kapsamlarda olsa bile çakışır.
|
||||||
|
// (fibonacci.sqt'de bu durum yok; gelecekte scope-aware slot atama gerekir.)
|
||||||
|
// - Sadece int değerler desteklenir (Value.kind şu an hep Int).
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
#ifndef SAQUT_IR_GENERATOR
|
||||||
|
#define SAQUT_IR_GENERATOR
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include "ir/ir_program.hpp"
|
||||||
|
#include "symbol/symbol_table.hpp"
|
||||||
|
#include "parser/ast_node.hpp"
|
||||||
|
|
||||||
|
class IRGenerator {
|
||||||
|
public:
|
||||||
|
// Ana giriş noktası: programNode = ProgramNode, tablo = sembol tablosu
|
||||||
|
IRProgram generate(ASTNode* programNode, SymbolTable& symbolTable);
|
||||||
|
|
||||||
|
private:
|
||||||
|
// ── Fonksiyon üretimi ─────────────────────────────────────────────────
|
||||||
|
void generateFunction(ASTNode* functionDeclNode);
|
||||||
|
|
||||||
|
// ── Deyim (statement) üretimi — talimat listesine yazar ──────────────
|
||||||
|
void generateStatement(ASTNode* node);
|
||||||
|
|
||||||
|
// ── İfade (expression) üretimi — sonucun slotunu döndürür ────────────
|
||||||
|
// Sonuç her zaman bir slotta bulunur. Identifier zaten bir slotta,
|
||||||
|
// hesaplamalar freshSlot() ile yeni slot alır.
|
||||||
|
int generateExpression(ASTNode* node);
|
||||||
|
|
||||||
|
// ── İkili operatör (binary op) için ortak yardımcı ───────────────────
|
||||||
|
int generateBinaryArithmetic(Opcode opcode, ASTNode* leftNode, ASTNode* rightNode);
|
||||||
|
|
||||||
|
// ── Slot yönetimi ─────────────────────────────────────────────────────
|
||||||
|
int freshSlot(); // Yeni slot numarası al (nextSlot_++)
|
||||||
|
void registerVariable(const std::string& name, int slot); // name → slot kaydı
|
||||||
|
int lookupVariable(const std::string& name); // name → slot (bulunamazsa hata)
|
||||||
|
|
||||||
|
// ── Talimat yazma yardımcıları ────────────────────────────────────────
|
||||||
|
// Talimatları currentFunction_->instructions'a ekler.
|
||||||
|
|
||||||
|
void emitLoadConst(int destSlot, int value);
|
||||||
|
void emitLoadSlot(int destSlot, int srcSlot);
|
||||||
|
void emitBinaryOp(Opcode op, int destSlot, int leftSlot, int rightSlot);
|
||||||
|
void emitReturn(int srcSlot);
|
||||||
|
// Koşulsuz atlama yazar; instruction indeksini döndürür (backpatch için).
|
||||||
|
// Hedef bilinmiyorsa -1 geçilir, patchJump() ile doldurulur.
|
||||||
|
int emitJumpUnconditional(int targetInstrIndex);
|
||||||
|
|
||||||
|
// JIF_FALSE talimatını -1 hedefle yazar, instruction indeksini döndürür.
|
||||||
|
// Döndürülen indeks ileride patchJump() ile doldurulur (backpatch).
|
||||||
|
int emitJumpIfFalse(int condSlot);
|
||||||
|
|
||||||
|
// Daha önce -1 hedefle yazılan jump'ın hedefini şu anki pozisyona doldur.
|
||||||
|
void patchJump(int instrIndex);
|
||||||
|
|
||||||
|
// Şu an kaç talimat üretildi? (jump hedefi belirlemek için)
|
||||||
|
int currentInstrIndex() const;
|
||||||
|
|
||||||
|
// ── Per-function üretim durumu ────────────────────────────────────────
|
||||||
|
IRFunction* currentFunction_ = nullptr; // şu an üretilen fonksiyon
|
||||||
|
int nextSlot_ = 0; // sıradaki boş slot numarası
|
||||||
|
|
||||||
|
// Değişken ismi → slot numarası.
|
||||||
|
// Sınırlama: aynı isimdeki farklı scope değişkenleri çakışır (TODO).
|
||||||
|
std::unordered_map<std::string, int> nameToSlot_;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // SAQUT_IR_GENERATOR
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
#include "ir/ir_program.hpp"
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
void IRProgram::dump() const {
|
||||||
|
std::cout << "IR DUMP\n\n";
|
||||||
|
for (const auto& name : functionOrder) {
|
||||||
|
auto it = functions.find(name);
|
||||||
|
if (it != functions.end()) it->second.dump();
|
||||||
|
}
|
||||||
|
std::cout << "END\n";
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,48 @@
|
||||||
|
// ============================================================================
|
||||||
|
// saQut IR — IRProgram (Bir .sqt Dosyasının Tüm IR İçeriği)
|
||||||
|
//
|
||||||
|
// IRProgram, üretilen tüm fonksiyonları tutar.
|
||||||
|
// Interpreter programı çalıştırmak için bu yapıyı kullanır.
|
||||||
|
//
|
||||||
|
// NEDEN İKİ YAPIDA TUTUYORUZ?
|
||||||
|
// - functionOrder: fonksiyonları tanımlandıkları sırayla tutar (dump için)
|
||||||
|
// - functions (unordered_map): CALL instruction'larında isimle hızlı arama için
|
||||||
|
//
|
||||||
|
// NOT: unordered_map değer semantiğiyle (IRFunction by value) tutar.
|
||||||
|
// findFunction() bir pointer döndürür — bu pointer tüm addFunction() çağrıları
|
||||||
|
// bittikten sonra alınmalıdır. Interpreter program üretildikten sonra çalıştığı
|
||||||
|
// için bu kural otomatik olarak sağlanır.
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
#ifndef SAQUT_IR_PROGRAM
|
||||||
|
#define SAQUT_IR_PROGRAM
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <vector>
|
||||||
|
#include "ir/ir_function.hpp"
|
||||||
|
|
||||||
|
struct IRProgram {
|
||||||
|
// Fonksiyon adı → IRFunction (hızlı arama için)
|
||||||
|
std::unordered_map<std::string, IRFunction> functions;
|
||||||
|
|
||||||
|
// Ekleme sırası (dump'ta orijinal sırayla göstermek için)
|
||||||
|
std::vector<std::string> functionOrder;
|
||||||
|
|
||||||
|
// Yeni fonksiyon ekle
|
||||||
|
void addFunction(IRFunction fn) {
|
||||||
|
functionOrder.push_back(fn.name);
|
||||||
|
functions.emplace(fn.name, std::move(fn));
|
||||||
|
}
|
||||||
|
|
||||||
|
// İsimle ara — bulunamazsa nullptr döner
|
||||||
|
IRFunction* findFunction(const std::string& name) {
|
||||||
|
auto it = functions.find(name);
|
||||||
|
return (it != functions.end()) ? &it->second : nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tüm fonksiyonları ekleme sırasıyla yazdır
|
||||||
|
void dump() const;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // SAQUT_IR_PROGRAM
|
||||||
|
|
@ -0,0 +1,332 @@
|
||||||
|
// ============================================================================
|
||||||
|
// saQut Compiler — AST Analizi ve Sembol Toplayıcı
|
||||||
|
// ============================================================================
|
||||||
|
//
|
||||||
|
// DİZİN: src/json.hpp
|
||||||
|
// KATMAN: Tüm katmanlardan sonra — AST'yi tüketir
|
||||||
|
// BAĞIMLI: AST (src/parser/ast.hpp)
|
||||||
|
// KULLANAN: main.cpp
|
||||||
|
//
|
||||||
|
// AMAÇ:
|
||||||
|
// 1. AST üzerinde analiz yap (düğüm sayısı, derinlik, tip dağılımı)
|
||||||
|
// 2. Sembolleri topla (fonksiyon isimleri, dönüş tipleri, değişkenler)
|
||||||
|
//
|
||||||
|
// NOT: JSON serileştirme ASTNode::toJson() tarafından yapılır.
|
||||||
|
// Bu dosya sadece analiz ve sembol toplama işlemlerini içerir.
|
||||||
|
// Yeni bir AST düğümü eklendiğinde buraya dokunmak gerekmez.
|
||||||
|
//
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
#ifndef SAQUT_JSON
|
||||||
|
#define SAQUT_JSON
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <sstream>
|
||||||
|
#include <map>
|
||||||
|
#include <vector>
|
||||||
|
#include "parser/ast.hpp"
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// astKindName — ASTKind enum'unu string'e çevir (analiz çıktısı için)
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
inline const char* astKindName(ASTKind k) {
|
||||||
|
switch (k) {
|
||||||
|
case ASTKind::Program: return "Program";
|
||||||
|
case ASTKind::FunctionDecl: return "FunctionDecl";
|
||||||
|
case ASTKind::Block: return "Block";
|
||||||
|
case ASTKind::VariableDecl: return "VariableDecl";
|
||||||
|
case ASTKind::BinaryExpression: return "BinaryExpression";
|
||||||
|
case ASTKind::UnaryExpression: return "UnaryExpression";
|
||||||
|
case ASTKind::Literal: return "Literal";
|
||||||
|
case ASTKind::Identifier: return "Identifier";
|
||||||
|
case ASTKind::Postfix: return "Postfix";
|
||||||
|
case ASTKind::IfStatement: return "IfStatement";
|
||||||
|
case ASTKind::ForStatement: return "ForStatement";
|
||||||
|
case ASTKind::WhileStatement: return "WhileStatement";
|
||||||
|
case ASTKind::DoWhileStatement: return "DoWhileStatement";
|
||||||
|
case ASTKind::ReturnStatement: return "ReturnStatement";
|
||||||
|
case ASTKind::BreakStatement: return "BreakStatement";
|
||||||
|
case ASTKind::ContinueStatement: return "ContinueStatement";
|
||||||
|
case ASTKind::ExpressionStatement: return "ExpressionStatement";
|
||||||
|
case ASTKind::Call: return "Call";
|
||||||
|
case ASTKind::MemberAccess: return "MemberAccess";
|
||||||
|
case ASTKind::IndexExpression: return "IndexExpression";
|
||||||
|
case ASTKind::StructDecl: return "StructDecl";
|
||||||
|
default: return "Unknown";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// AST → JSON (ince sarmalayıcı — asıl iş ASTNode::toJson()'da)
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
inline std::string astToJson(ASTNode* node, int depth = 0) {
|
||||||
|
if (!node) return "null";
|
||||||
|
return node->toJson(depth);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// AST Analizi
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
struct AstAnalysis {
|
||||||
|
int totalNodes = 0;
|
||||||
|
int maxDepth = 0;
|
||||||
|
int functionCount = 0;
|
||||||
|
int variableCount = 0;
|
||||||
|
int ifCount = 0;
|
||||||
|
int loopCount = 0; // for + while + do-while
|
||||||
|
std::map<std::string, int> nodeTypeCounts;
|
||||||
|
};
|
||||||
|
|
||||||
|
inline void analyzeRecursive(ASTNode* node, int currentDepth, AstAnalysis& a) {
|
||||||
|
if (!node) return;
|
||||||
|
|
||||||
|
a.totalNodes++;
|
||||||
|
if (currentDepth > a.maxDepth) a.maxDepth = currentDepth;
|
||||||
|
|
||||||
|
const char* name = astKindName(node->kind);
|
||||||
|
a.nodeTypeCounts[name]++;
|
||||||
|
|
||||||
|
switch (node->kind) {
|
||||||
|
case ASTKind::FunctionDecl: a.functionCount++; break;
|
||||||
|
case ASTKind::VariableDecl: a.variableCount++; break;
|
||||||
|
case ASTKind::IfStatement: a.ifCount++; break;
|
||||||
|
case ASTKind::WhileStatement:
|
||||||
|
case ASTKind::ForStatement:
|
||||||
|
case ASTKind::DoWhileStatement:
|
||||||
|
a.loopCount++; break;
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tüm çocukları gez
|
||||||
|
switch (node->kind) {
|
||||||
|
case ASTKind::Program:
|
||||||
|
case ASTKind::FunctionDecl:
|
||||||
|
case ASTKind::Block: {
|
||||||
|
for (auto* child : node->getChildren())
|
||||||
|
analyzeRecursive(child, currentDepth + 1, a);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ASTKind::VariableDecl: {
|
||||||
|
auto* vd = (VariableDeclNode*)node;
|
||||||
|
if (vd->initExpr) analyzeRecursive(vd->initExpr, currentDepth + 1, a);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ASTKind::BinaryExpression: {
|
||||||
|
auto* bin = (BinaryExpressionNode*)node;
|
||||||
|
if (bin->Left) analyzeRecursive(bin->Left, currentDepth + 1, a);
|
||||||
|
if (bin->Right) analyzeRecursive(bin->Right, currentDepth + 1, a);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ASTKind::Postfix: {
|
||||||
|
auto* pf = (PostfixNode*)node;
|
||||||
|
if (pf->operand) analyzeRecursive(pf->operand, currentDepth + 1, a);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ASTKind::IfStatement: {
|
||||||
|
auto* ifn = (IfStatementNode*)node;
|
||||||
|
if (ifn->condition) analyzeRecursive(ifn->condition, currentDepth + 1, a);
|
||||||
|
if (ifn->thenBranch) analyzeRecursive(ifn->thenBranch, currentDepth + 1, a);
|
||||||
|
if (ifn->elseBranch) analyzeRecursive(ifn->elseBranch, currentDepth + 1, a);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ASTKind::WhileStatement: {
|
||||||
|
auto* ws = (WhileStatementNode*)node;
|
||||||
|
if (ws->condition) analyzeRecursive(ws->condition, currentDepth + 1, a);
|
||||||
|
if (ws->body) analyzeRecursive(ws->body, currentDepth + 1, a);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ASTKind::ForStatement: {
|
||||||
|
auto* fs = (ForStatementNode*)node;
|
||||||
|
if (fs->init) analyzeRecursive(fs->init, currentDepth + 1, a);
|
||||||
|
if (fs->condition) analyzeRecursive(fs->condition, currentDepth + 1, a);
|
||||||
|
if (fs->update) analyzeRecursive(fs->update, currentDepth + 1, a);
|
||||||
|
if (fs->body) analyzeRecursive(fs->body, currentDepth + 1, a);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ASTKind::DoWhileStatement: {
|
||||||
|
auto* dw = (DoWhileStatementNode*)node;
|
||||||
|
if (dw->body) analyzeRecursive(dw->body, currentDepth + 1, a);
|
||||||
|
if (dw->condition) analyzeRecursive(dw->condition, currentDepth + 1, a);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ASTKind::ReturnStatement: {
|
||||||
|
auto* rs = (ReturnStatementNode*)node;
|
||||||
|
if (rs->value) analyzeRecursive(rs->value, currentDepth + 1, a);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ASTKind::ExpressionStatement: {
|
||||||
|
auto* es = (ExpressionStatementNode*)node;
|
||||||
|
if (es->expression) analyzeRecursive(es->expression, currentDepth + 1, a);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ASTKind::Call: {
|
||||||
|
auto* call = (CallExpressionNode*)node;
|
||||||
|
if (call->callee) analyzeRecursive(call->callee, currentDepth + 1, a);
|
||||||
|
for (auto* arg : call->arguments) analyzeRecursive(arg, currentDepth + 1, a);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ASTKind::MemberAccess: {
|
||||||
|
auto* ma = (MemberAccessNode*)node;
|
||||||
|
if (ma->object) analyzeRecursive(ma->object, currentDepth + 1, a);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ASTKind::IndexExpression: {
|
||||||
|
auto* ie = (IndexExpressionNode*)node;
|
||||||
|
if (ie->object) analyzeRecursive(ie->object, currentDepth + 1, a);
|
||||||
|
if (ie->index) analyzeRecursive(ie->index, currentDepth + 1, a);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ASTKind::StructDecl: {
|
||||||
|
for (auto* child : node->getChildren()) analyzeRecursive(child, currentDepth + 1, a);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: break; // Literal, Identifier, Break, Continue — yaprak düğüm
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline AstAnalysis analyzeAst(ASTNode* root) {
|
||||||
|
AstAnalysis a;
|
||||||
|
analyzeRecursive(root, 0, a);
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline std::string analysisToJson(const AstAnalysis& a) {
|
||||||
|
std::ostringstream ss;
|
||||||
|
ss << " \"totalNodes\": " << a.totalNodes << ",\n"
|
||||||
|
<< " \"maxDepth\": " << a.maxDepth << ",\n"
|
||||||
|
<< " \"functionCount\": " << a.functionCount << ",\n"
|
||||||
|
<< " \"variableCount\": " << a.variableCount << ",\n"
|
||||||
|
<< " \"ifCount\": " << a.ifCount << ",\n"
|
||||||
|
<< " \"loopCount\": " << a.loopCount << ",\n"
|
||||||
|
<< " \"nodeTypes\": {\n";
|
||||||
|
bool first = true;
|
||||||
|
for (auto& [name, count] : a.nodeTypeCounts) {
|
||||||
|
if (!first) ss << ",\n";
|
||||||
|
ss << " \"" << name << "\": " << count;
|
||||||
|
first = false;
|
||||||
|
}
|
||||||
|
ss << "\n }";
|
||||||
|
return ss.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// Sembol Toplayıcı
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
struct SymbolEntry {
|
||||||
|
std::string name;
|
||||||
|
std::string kind; // "function" veya "variable"
|
||||||
|
std::string type; // Dönüş tipi veya değişken tipi
|
||||||
|
};
|
||||||
|
|
||||||
|
inline void collectSymbolsRecursive(ASTNode* node, std::vector<SymbolEntry>& symbols) {
|
||||||
|
if (!node) return;
|
||||||
|
|
||||||
|
// Sadece FunctionDecl ve VariableDecl toplanır — diğerleri gezilir
|
||||||
|
if (node->kind == ASTKind::FunctionDecl) {
|
||||||
|
auto* fn = (FunctionDeclNode*)node;
|
||||||
|
symbols.push_back({fn->name, "function", fn->returnType});
|
||||||
|
} else if (node->kind == ASTKind::StructDecl) {
|
||||||
|
auto* st = (StructDeclNode*)node;
|
||||||
|
symbols.push_back({st->name, "struct", ""});
|
||||||
|
} else if (node->kind == ASTKind::VariableDecl) {
|
||||||
|
auto* vd = (VariableDeclNode*)node;
|
||||||
|
symbols.push_back({vd->name, "variable", vd->varType});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Çocukları gez
|
||||||
|
switch (node->kind) {
|
||||||
|
case ASTKind::Program:
|
||||||
|
case ASTKind::FunctionDecl:
|
||||||
|
case ASTKind::Block:
|
||||||
|
for (auto* child : node->getChildren())
|
||||||
|
collectSymbolsRecursive(child, symbols);
|
||||||
|
break;
|
||||||
|
case ASTKind::VariableDecl: {
|
||||||
|
auto* vd = (VariableDeclNode*)node;
|
||||||
|
if (vd->initExpr) collectSymbolsRecursive(vd->initExpr, symbols);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ASTKind::BinaryExpression: {
|
||||||
|
auto* bin = (BinaryExpressionNode*)node;
|
||||||
|
if (bin->Left) collectSymbolsRecursive(bin->Left, symbols);
|
||||||
|
if (bin->Right) collectSymbolsRecursive(bin->Right, symbols);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ASTKind::Postfix: {
|
||||||
|
auto* pf = (PostfixNode*)node;
|
||||||
|
if (pf->operand) collectSymbolsRecursive(pf->operand, symbols);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ASTKind::IfStatement: {
|
||||||
|
auto* ifn = (IfStatementNode*)node;
|
||||||
|
if (ifn->condition) collectSymbolsRecursive(ifn->condition, symbols);
|
||||||
|
if (ifn->thenBranch) collectSymbolsRecursive(ifn->thenBranch, symbols);
|
||||||
|
if (ifn->elseBranch) collectSymbolsRecursive(ifn->elseBranch, symbols);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ASTKind::WhileStatement: {
|
||||||
|
auto* ws = (WhileStatementNode*)node;
|
||||||
|
if (ws->condition) collectSymbolsRecursive(ws->condition, symbols);
|
||||||
|
if (ws->body) collectSymbolsRecursive(ws->body, symbols);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ASTKind::ForStatement: {
|
||||||
|
auto* fs = (ForStatementNode*)node;
|
||||||
|
if (fs->init) collectSymbolsRecursive(fs->init, symbols);
|
||||||
|
if (fs->condition) collectSymbolsRecursive(fs->condition, symbols);
|
||||||
|
if (fs->update) collectSymbolsRecursive(fs->update, symbols);
|
||||||
|
if (fs->body) collectSymbolsRecursive(fs->body, symbols);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ASTKind::DoWhileStatement: {
|
||||||
|
auto* dw = (DoWhileStatementNode*)node;
|
||||||
|
if (dw->body) collectSymbolsRecursive(dw->body, symbols);
|
||||||
|
if (dw->condition) collectSymbolsRecursive(dw->condition, symbols);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ASTKind::ReturnStatement: {
|
||||||
|
auto* rs = (ReturnStatementNode*)node;
|
||||||
|
if (rs->value) collectSymbolsRecursive(rs->value, symbols);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ASTKind::ExpressionStatement: {
|
||||||
|
auto* es = (ExpressionStatementNode*)node;
|
||||||
|
if (es->expression) collectSymbolsRecursive(es->expression, symbols);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ASTKind::Call: {
|
||||||
|
auto* call = (CallExpressionNode*)node;
|
||||||
|
if (call->callee) collectSymbolsRecursive(call->callee, symbols);
|
||||||
|
for (auto* arg : call->arguments) collectSymbolsRecursive(arg, symbols);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ASTKind::MemberAccess: {
|
||||||
|
auto* ma = (MemberAccessNode*)node;
|
||||||
|
if (ma->object) collectSymbolsRecursive(ma->object, symbols);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ASTKind::IndexExpression: {
|
||||||
|
auto* ie = (IndexExpressionNode*)node;
|
||||||
|
if (ie->object) collectSymbolsRecursive(ie->object, symbols);
|
||||||
|
if (ie->index) collectSymbolsRecursive(ie->index, symbols);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ASTKind::StructDecl: {
|
||||||
|
for (auto* child : node->getChildren()) collectSymbolsRecursive(child, symbols);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline std::vector<SymbolEntry> collectSymbols(ASTNode* root) {
|
||||||
|
std::vector<SymbolEntry> symbols;
|
||||||
|
collectSymbolsRecursive(root, symbols);
|
||||||
|
return symbols;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // SAQUT_JSON
|
||||||
|
|
@ -0,0 +1,346 @@
|
||||||
|
#include "lexer/lexer.hpp"
|
||||||
|
|
||||||
|
// --------------------------------------------------------------------------
|
||||||
|
// beginPosition: Mevcut offset'i yığına kaydet.
|
||||||
|
// İç içe çağrılabilir: 3 kere beginPosition → 3 elemanlı yığın.
|
||||||
|
// --------------------------------------------------------------------------
|
||||||
|
void Lexer::beginPosition() {
|
||||||
|
offsetMap.push_back(getLastPosition());
|
||||||
|
}
|
||||||
|
|
||||||
|
// --------------------------------------------------------------------------
|
||||||
|
// getLastPosition: Yığının tepesindeki konumu döndür.
|
||||||
|
// Yığın boşsa mevcut offset'i döndür (başlangıç durumu).
|
||||||
|
// --------------------------------------------------------------------------
|
||||||
|
int Lexer::getLastPosition() {
|
||||||
|
if (offsetMap.empty()) return offset;
|
||||||
|
return offsetMap.back();
|
||||||
|
}
|
||||||
|
|
||||||
|
// --------------------------------------------------------------------------
|
||||||
|
// acceptPosition: Yığındaki son geçici konumu kalıcı yap.
|
||||||
|
// Örnek: offsetMap=[5,10], offset=15 → offsetMap.back()=10 olur.
|
||||||
|
// Bu sayede include() denemesi başarılı olduğunda konum ilerletilmiş olur.
|
||||||
|
// --------------------------------------------------------------------------
|
||||||
|
void Lexer::acceptPosition() {
|
||||||
|
int t = offsetMap.back();
|
||||||
|
setLastPosition(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
// --------------------------------------------------------------------------
|
||||||
|
// setLastPosition: Yığının tepesini veya offset'i değiştir.
|
||||||
|
// --------------------------------------------------------------------------
|
||||||
|
void Lexer::setLastPosition(int n) {
|
||||||
|
if (offsetMap.empty())
|
||||||
|
offset = n;
|
||||||
|
else
|
||||||
|
offsetMap.back() = n;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --------------------------------------------------------------------------
|
||||||
|
// isEnd: Dosya sonuna gelindi mi? offset >= size.
|
||||||
|
// --------------------------------------------------------------------------
|
||||||
|
bool Lexer::isEnd() {
|
||||||
|
return size <= getOffset();
|
||||||
|
}
|
||||||
|
|
||||||
|
// --------------------------------------------------------------------------
|
||||||
|
// rejectPosition: Yığındaki son konumu at. Başarısız include() denemesi sonrası.
|
||||||
|
// --------------------------------------------------------------------------
|
||||||
|
void Lexer::rejectPosition() {
|
||||||
|
offsetMap.pop_back();
|
||||||
|
}
|
||||||
|
|
||||||
|
// --------------------------------------------------------------------------
|
||||||
|
// positionRange: Yığındaki en dış ve en iç konumu [start, end] olarak döndür.
|
||||||
|
// UYARI: new int[2] ile heap'te tahsis eder. Çağıran sorumludur.
|
||||||
|
// TODO: std::pair<int,int> veya yapı kullanarak tahsisi kaldır.
|
||||||
|
// --------------------------------------------------------------------------
|
||||||
|
int* Lexer::positionRange() {
|
||||||
|
int len = offsetMap.size();
|
||||||
|
if (len == 0)
|
||||||
|
return new int[2]{0, offset};
|
||||||
|
if (len == 1)
|
||||||
|
return new int[2]{offset, offsetMap[0]};
|
||||||
|
return new int[2]{offsetMap[len - 2], offsetMap[len - 1]};
|
||||||
|
}
|
||||||
|
|
||||||
|
// --------------------------------------------------------------------------
|
||||||
|
// getPositionRange: positionRange() aralığındaki metni string olarak döndür.
|
||||||
|
// --------------------------------------------------------------------------
|
||||||
|
std::string Lexer::getPositionRange() {
|
||||||
|
int* a = positionRange();
|
||||||
|
std::string mem;
|
||||||
|
for (int i = a[0]; i < a[1]; i++)
|
||||||
|
mem.push_back(input.at(i));
|
||||||
|
return mem;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --------------------------------------------------------------------------
|
||||||
|
// include: Belirtilen kelime mevcut konumda başlıyor mu?
|
||||||
|
// --------------------------------------------------------------------------
|
||||||
|
bool Lexer::include(std::string_view word, bool accept) {
|
||||||
|
beginPosition();
|
||||||
|
for (size_t i = 0; i < word.size(); i++) {
|
||||||
|
if (isEnd()) {
|
||||||
|
rejectPosition();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (word[i] != getchar()) {
|
||||||
|
rejectPosition();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
nextChar();
|
||||||
|
}
|
||||||
|
if (accept)
|
||||||
|
acceptPosition();
|
||||||
|
else
|
||||||
|
rejectPosition();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --------------------------------------------------------------------------
|
||||||
|
// getOffset / setOffset: Konum erişimcileri.
|
||||||
|
// --------------------------------------------------------------------------
|
||||||
|
int Lexer::getOffset() {
|
||||||
|
return getLastPosition();
|
||||||
|
}
|
||||||
|
|
||||||
|
int Lexer::setOffset(int n) {
|
||||||
|
setLastPosition(n);
|
||||||
|
return getLastPosition();
|
||||||
|
}
|
||||||
|
|
||||||
|
// --------------------------------------------------------------------------
|
||||||
|
// getchar(additionalOffset): offset + ek kadar ilerideki karakteri oku.
|
||||||
|
// --------------------------------------------------------------------------
|
||||||
|
char Lexer::getchar(int additionalOffset) {
|
||||||
|
int target = getOffset() + additionalOffset;
|
||||||
|
if (target >= size) {
|
||||||
|
std::cerr << "Lexer hatası: sınır aşımı\n";
|
||||||
|
return '\0';
|
||||||
|
}
|
||||||
|
return input.at(target);
|
||||||
|
}
|
||||||
|
|
||||||
|
char Lexer::getchar() {
|
||||||
|
int target = getOffset();
|
||||||
|
if (target >= size) {
|
||||||
|
std::cerr << "Lexer hatası: sınır aşımı\n";
|
||||||
|
return '\0';
|
||||||
|
}
|
||||||
|
return input.at(target);
|
||||||
|
}
|
||||||
|
|
||||||
|
// --------------------------------------------------------------------------
|
||||||
|
// nextChar / toChar: Konum ilerletme.
|
||||||
|
// --------------------------------------------------------------------------
|
||||||
|
void Lexer::nextChar() {
|
||||||
|
if (!isEnd())
|
||||||
|
setOffset(getOffset() + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Lexer::toChar(int n) {
|
||||||
|
if (!isEnd())
|
||||||
|
setOffset(getOffset() + n);
|
||||||
|
}
|
||||||
|
|
||||||
|
// --------------------------------------------------------------------------
|
||||||
|
// getLocation: Mevcut offset'in SourceLocation'ını döndür.
|
||||||
|
// --------------------------------------------------------------------------
|
||||||
|
SourceLocation Lexer::getLocation() {
|
||||||
|
return sourceFile.offsetToLocation(getOffset());
|
||||||
|
}
|
||||||
|
|
||||||
|
// --------------------------------------------------------------------------
|
||||||
|
// setSourceText: Yeni kaynak kodu yükle ve SourceFile'ı güncelle.
|
||||||
|
// --------------------------------------------------------------------------
|
||||||
|
void Lexer::setSourceText(const std::string& path, const std::string& text) {
|
||||||
|
sourceFile.setText(path, text);
|
||||||
|
setText(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
// --------------------------------------------------------------------------
|
||||||
|
// setText: Yeni kaynak kodu yükle. input ve size'ı günceller.
|
||||||
|
// --------------------------------------------------------------------------
|
||||||
|
void Lexer::setText(std::string text) {
|
||||||
|
input = text;
|
||||||
|
size = static_cast<int>(text.length());
|
||||||
|
}
|
||||||
|
|
||||||
|
// --------------------------------------------------------------------------
|
||||||
|
// skipWhiteSpace: Boşluk, sekme, satırsonu, satırbaşı karakterlerini atla.
|
||||||
|
// --------------------------------------------------------------------------
|
||||||
|
void Lexer::skipWhiteSpace() {
|
||||||
|
while (!isEnd()) {
|
||||||
|
switch (getchar()) {
|
||||||
|
case '\r': // carriage return (Windows satırsonu \r\n)
|
||||||
|
case '\n': // line feed (Unix satırsonu)
|
||||||
|
case '\b': // backspace
|
||||||
|
case '\t': // tab
|
||||||
|
case ' ': // boşluk
|
||||||
|
nextChar();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --------------------------------------------------------------------------
|
||||||
|
// isNumeric: Mevcut karakter bir rakam mı? (0-9)
|
||||||
|
// --------------------------------------------------------------------------
|
||||||
|
bool Lexer::isNumeric() {
|
||||||
|
char c = getchar();
|
||||||
|
return (c >= '0' && c <= '9');
|
||||||
|
}
|
||||||
|
|
||||||
|
// --------------------------------------------------------------------------
|
||||||
|
// readNumeric: Tam bir sayı literal'ı oku.
|
||||||
|
// --------------------------------------------------------------------------
|
||||||
|
INumber Lexer::readNumeric() {
|
||||||
|
INumber num;
|
||||||
|
num.start = getLastPosition();
|
||||||
|
num.startLoc = getLocation();
|
||||||
|
|
||||||
|
// --- Adım 1: İsteğe bağlı işaret ---
|
||||||
|
if (getchar() == '-') {
|
||||||
|
nextChar();
|
||||||
|
num.positive = false;
|
||||||
|
} else if (getchar() == '+') {
|
||||||
|
nextChar();
|
||||||
|
num.positive = true;
|
||||||
|
} else {
|
||||||
|
num.positive = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Adım 2: İlk karakter '0' ise özel format kontrolü ---
|
||||||
|
bool nextDot = false;
|
||||||
|
if (getchar() == '0') {
|
||||||
|
num.token.push_back('0');
|
||||||
|
nextChar();
|
||||||
|
char c = getchar();
|
||||||
|
switch (c) {
|
||||||
|
case 'x': case 'X': // Hex: 0xFF, 0X1A
|
||||||
|
num.token.push_back(c);
|
||||||
|
num.base = 16;
|
||||||
|
nextChar();
|
||||||
|
break;
|
||||||
|
case 'b': case 'B': // Binary: 0b1010
|
||||||
|
num.token.push_back(c);
|
||||||
|
num.base = 2;
|
||||||
|
nextChar();
|
||||||
|
break;
|
||||||
|
case '.': // Float: 0.5, 0.0
|
||||||
|
num.token.push_back(c);
|
||||||
|
num.base = 10;
|
||||||
|
nextDot = true;
|
||||||
|
num.isFloat = true;
|
||||||
|
nextChar();
|
||||||
|
break;
|
||||||
|
case '0': case '1': case '2': case '3': case '4':
|
||||||
|
case '5': case '6': case '7':
|
||||||
|
// Octal: 0777 — sonraki karakter octal rakam ise devam et
|
||||||
|
num.base = 8;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
num.end = getLastPosition();
|
||||||
|
num.endLoc = getLocation();
|
||||||
|
return num;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
num.base = 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Adım 3: Ana okuma döngüsü ---
|
||||||
|
while (!isEnd()) {
|
||||||
|
char c = getchar();
|
||||||
|
switch (c) {
|
||||||
|
case '0':
|
||||||
|
case '1':
|
||||||
|
num.token.push_back(c);
|
||||||
|
break;
|
||||||
|
case '2': case '3': case '4': case '5':
|
||||||
|
case '6': case '7':
|
||||||
|
if (num.base >= 8)
|
||||||
|
num.token.push_back(c);
|
||||||
|
else {
|
||||||
|
num.end = getLastPosition();
|
||||||
|
num.endLoc = getLocation();
|
||||||
|
return num;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case '8': case '9':
|
||||||
|
if (num.base >= 10)
|
||||||
|
num.token.push_back(c);
|
||||||
|
else {
|
||||||
|
num.end = getLastPosition();
|
||||||
|
num.endLoc = getLocation();
|
||||||
|
return num;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'a': case 'A': case 'b': case 'B':
|
||||||
|
case 'c': case 'C': case 'd': case 'D':
|
||||||
|
case 'f': case 'F':
|
||||||
|
if (num.base >= 16)
|
||||||
|
num.token.push_back(c);
|
||||||
|
else {
|
||||||
|
num.end = getLastPosition();
|
||||||
|
num.endLoc = getLocation();
|
||||||
|
return num;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case '.':
|
||||||
|
if (!nextDot) {
|
||||||
|
if (num.token.empty())
|
||||||
|
num.token += "0.";
|
||||||
|
else
|
||||||
|
num.token.push_back('.');
|
||||||
|
nextDot = true;
|
||||||
|
num.isFloat = true;
|
||||||
|
} else {
|
||||||
|
num.end = getLastPosition();
|
||||||
|
num.endLoc = getLocation();
|
||||||
|
return num;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'e': case 'E':
|
||||||
|
if (num.base == 16) {
|
||||||
|
num.token.push_back(c);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (num.base == 10) {
|
||||||
|
num.hasEpsilon = true;
|
||||||
|
num.token.push_back(c);
|
||||||
|
nextChar();
|
||||||
|
c = getchar();
|
||||||
|
if (c == '+' || c == '-') {
|
||||||
|
num.token.push_back(c);
|
||||||
|
nextChar();
|
||||||
|
}
|
||||||
|
while (!isEnd()) {
|
||||||
|
c = getchar();
|
||||||
|
if (c >= '0' && c <= '9') {
|
||||||
|
num.token.push_back(c);
|
||||||
|
nextChar();
|
||||||
|
} else {
|
||||||
|
num.end = getLastPosition();
|
||||||
|
num.endLoc = getLocation();
|
||||||
|
return num;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
num.end = getLastPosition();
|
||||||
|
num.endLoc = getLocation();
|
||||||
|
return num;
|
||||||
|
default:
|
||||||
|
num.end = getLastPosition();
|
||||||
|
return num;
|
||||||
|
}
|
||||||
|
nextChar();
|
||||||
|
}
|
||||||
|
num.end = getLastPosition();
|
||||||
|
num.endLoc = getLocation();
|
||||||
|
return num;
|
||||||
|
}
|
||||||
|
|
@ -15,46 +15,6 @@
|
||||||
// 4. Boşluk karakterlerini atlar
|
// 4. Boşluk karakterlerini atlar
|
||||||
// 5. Satır/sütun bilgisi sağlar (hata mesajları için temel)
|
// 5. Satır/sütun bilgisi sağlar (hata mesajları için temel)
|
||||||
//
|
//
|
||||||
// ADR-006: Neden Kendi Lexer'ımız?
|
|
||||||
// - std::istringstream veya regex kullanmak yerine, tam kontrol sağlayan
|
|
||||||
// sıfırdan bir lexer yazdık.
|
|
||||||
// - Backtracking: offsetMap ile konum yığını tutar, denenen bir eşleşme
|
|
||||||
// başarısız olursa geri alınabilir. Bu özellik std::istream'de yoktur.
|
|
||||||
// - Performans: Sanal fonksiyon çağrısı yok, her şey inline.
|
|
||||||
// - Hata ayıklama: Her karakter okuması kontrol edilebilir.
|
|
||||||
//
|
|
||||||
// TASARIM KARARLARI:
|
|
||||||
// 1. offsetMap (std::vector<int>): İç içe backtracking için yığın.
|
|
||||||
// beginPosition() → yığına mevcut konumu ekler
|
|
||||||
// acceptPosition() → yığındaki son konumu kalıcı yapar
|
|
||||||
// rejectPosition() → yığındaki son konumu atar (geri al)
|
|
||||||
// Bu sayede "dene, başarısız olursa geri al" patterni çalışır.
|
|
||||||
//
|
|
||||||
// 2. getchar() iki overload:
|
|
||||||
// - getchar(): mevcut konumdaki karakteri okur
|
|
||||||
// - getchar(int offset): mevcut konum + offset'teki karakteri okur
|
|
||||||
// İkincisi özellikle keyword kontrolünde önemlidir:
|
|
||||||
// "do" kelimesini gördükten sonra, bunun "double"ın başlangıcı olmadığını
|
|
||||||
// kontrol etmek için keyword sonrası karaktere bakılır.
|
|
||||||
//
|
|
||||||
// 3. isEnd(): offset >= size ile kontrol. offset her zaman [0, size] aralığında.
|
|
||||||
// size konumunda EOF (end of file) anlamına gelir.
|
|
||||||
//
|
|
||||||
// 4. readNumeric(): C/C++/Java sayı formatlarını destekler:
|
|
||||||
// - Decimal: 42, -3, +7
|
|
||||||
// - Hex: 0xFF, 0X1A
|
|
||||||
// - Binary: 0b1010, 0B1100
|
|
||||||
// - Octal: 0777 (0 ile başlayan ve 8-9 içermeyen)
|
|
||||||
// - Float: 3.14, .5, 1e10, 2.5E-3
|
|
||||||
// - Negatif/Pozitif: -42, +3 (baştaki işaret)
|
|
||||||
//
|
|
||||||
// BİLİNEN SINIRLAMALAR (TODO):
|
|
||||||
// TODO: Satır/sütun takibi eklenmeli (şu anda sadece offset var)
|
|
||||||
// TODO: Unicode/UTF-8 desteği (şu anda sadece ASCII)
|
|
||||||
// TODO: ' char literal'ı okunamıyor
|
|
||||||
// TODO: Sayısal alt çizgi (_) ayracı: 1_000_000 formatı
|
|
||||||
// TODO: Binary floating point: 0b1.1p10 formatı (C99 hexfloat)
|
|
||||||
//
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|
||||||
#ifndef SAQUT_LEXER
|
#ifndef SAQUT_LEXER
|
||||||
|
|
@ -62,33 +22,20 @@
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <string_view>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include "core/location.hpp"
|
||||||
|
#include "core/sourcefile.hpp"
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// INumber — Ara Sayısal Veri Yapısı
|
// INumber — Ara Sayısal Veri Yapısı
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
//
|
|
||||||
// Lexer'ın readNumeric() fonksiyonu tarafından döndürülür.
|
|
||||||
// Tokenizer bu yapıyı NumberToken'a dönüştürür.
|
|
||||||
//
|
|
||||||
// Neden ayrı bir struct? Lexer katmanı Token sınıflarından haberdar değil.
|
|
||||||
// Bağımlılık yönü: Lexer ← Tokenizer. Lexer hiçbir üst katmanı include etmez.
|
|
||||||
//
|
|
||||||
// ALANLAR:
|
|
||||||
// start, end : Kaynak koddaki başlangıç/bitiş konumları (offset)
|
|
||||||
// token : Sayının ham string hali (örn: "0xFF", "3.14", "1e10")
|
|
||||||
// isFloat : Ondalıklı sayı mı? (nokta veya epsilon içeriyor mu)
|
|
||||||
// hasEpsilon : Bilimsel gösterim mi? (e/E içeriyor mu)
|
|
||||||
// base : Sayı tabanı: 2, 8, 10, veya 16
|
|
||||||
// - 0x/0X ile başlarsa 16
|
|
||||||
// - 0b/0B ile başlarsa 2
|
|
||||||
// - 0 ile başlayıp 8-9 içermiyorsa 8
|
|
||||||
// - diğer her şey 10
|
|
||||||
// positive : Pozitif mi? (başında - işareti yoksa true)
|
|
||||||
//
|
|
||||||
struct INumber {
|
struct INumber {
|
||||||
int start = 0; // Kaynak koddaki başlangıç offset'i
|
int start = 0; // Kaynak koddaki başlangıç offset'i
|
||||||
int end = 0; // Kaynak koddaki bitiş offset'i
|
int end = 0; // Kaynak koddaki bitiş offset'i
|
||||||
|
SourceLocation startLoc; // Kaynak koddaki başlangıç konumu (line, column)
|
||||||
|
SourceLocation endLoc; // Kaynak koddaki bitiş konumu
|
||||||
std::string token; // Sayının ham metni (örn: "42", "0xFF", "3.14e-2")
|
std::string token; // Sayının ham metni (örn: "42", "0xFF", "3.14e-2")
|
||||||
bool isFloat = false; // true ise float/double literal
|
bool isFloat = false; // true ise float/double literal
|
||||||
bool hasEpsilon = false; // true ise bilimsel gösterim (örn: 1e10)
|
bool hasEpsilon = false; // true ise bilimsel gösterim (örn: 1e10)
|
||||||
|
|
@ -99,21 +46,7 @@ struct INumber {
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// Lexer — Karakter Seviyesinde Tarayıcı
|
// Lexer — Karakter Seviyesinde Tarayıcı
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
//
|
|
||||||
// Derleyici pipeline'ının en alt katmanı. Ham string üzerinde çalışır.
|
|
||||||
// Üst katmanlara (Tokenizer) karakter okuma ve konum yönetimi hizmeti sunar.
|
|
||||||
//
|
|
||||||
// DURUM DEĞİŞKENLERİ:
|
|
||||||
// input : Taranan kaynak kodun tamamı (string kopyası, değişmez)
|
|
||||||
// size : input.length() önbelleği (performans: her seferinde hesaplamaz)
|
|
||||||
// offset : Mevcut okuma konumu. 0 = ilk karakter, size = EOF
|
|
||||||
// offsetMap : Backtracking yığını. İç içe beginPosition/acceptPosition/rejectPosition
|
|
||||||
//
|
|
||||||
// PERFORMANS NOTU:
|
|
||||||
// Tüm metotlar inline tanımlanmıştır. Sanal fonksiyon çağrısı yoktur.
|
|
||||||
// offset değişiklikleri O(1)'dir.
|
|
||||||
// include() metodu O(n) karakter karşılaştırması yapar (n = kelime uzunluğu).
|
|
||||||
//
|
|
||||||
class Lexer {
|
class Lexer {
|
||||||
public:
|
public:
|
||||||
// --- Ham Veri ---
|
// --- Ham Veri ---
|
||||||
|
|
@ -123,14 +56,6 @@ public:
|
||||||
std::vector<int> offsetMap; // Backtracking yığını
|
std::vector<int> offsetMap; // Backtracking yığını
|
||||||
|
|
||||||
// --- Pozisyon Yönetimi (Backtracking API) ---
|
// --- Pozisyon Yönetimi (Backtracking API) ---
|
||||||
//
|
|
||||||
// Kullanım örneği:
|
|
||||||
// lexer.beginPosition(); // konumu kaydet
|
|
||||||
// if (lexer.include("for", false)) // dene (false = eşleşse de geri al)
|
|
||||||
// lexer.acceptPosition(); // başarılı → kalıcı yap
|
|
||||||
// else
|
|
||||||
// lexer.rejectPosition(); // başarısız → geri al
|
|
||||||
|
|
||||||
void beginPosition(); // Şu anki konumu yığına kaydet
|
void beginPosition(); // Şu anki konumu yığına kaydet
|
||||||
int getLastPosition(); // Yığındaki son konumu döndür
|
int getLastPosition(); // Yığındaki son konumu döndür
|
||||||
void acceptPosition(); // Yığındaki son konumu kalıcı yap (apply)
|
void acceptPosition(); // Yığındaki son konumu kalıcı yap (apply)
|
||||||
|
|
@ -143,11 +68,7 @@ public:
|
||||||
std::string getPositionRange(); // Pozisyon aralığındaki metni döndür
|
std::string getPositionRange(); // Pozisyon aralığındaki metni döndür
|
||||||
|
|
||||||
// --- Desen Eşleme ---
|
// --- Desen Eşleme ---
|
||||||
// include(): Belirtilen kelime mevcut konumda başlıyor mu?
|
bool include(std::string_view word, bool accept = true);
|
||||||
// accept=true (varsayılan): eşleşirse konum ilerletilir
|
|
||||||
// accept=false: eşleşse bile konum geri alınır (keyword kontrolü için)
|
|
||||||
// Örnek: include("for", false) → "for" ile başlıyor mu? konumu değiştirme.
|
|
||||||
bool include(std::string word, bool accept = true);
|
|
||||||
|
|
||||||
// --- Konum Okuma/Yazma ---
|
// --- Konum Okuma/Yazma ---
|
||||||
int getOffset(); // Mevcut offset'i döndür
|
int getOffset(); // Mevcut offset'i döndür
|
||||||
|
|
@ -160,403 +81,14 @@ public:
|
||||||
void toChar(int n); // offset'i n kadar ilerlet
|
void toChar(int n); // offset'i n kadar ilerlet
|
||||||
|
|
||||||
// --- Üst Seviye İşlemler ---
|
// --- Üst Seviye İşlemler ---
|
||||||
|
SourceFile sourceFile; // Kaynak kod ve satır başı offset'leri
|
||||||
|
SourceLocation getLocation(); // Mevcut offset'in SourceLocation'ını döndür
|
||||||
|
void setSourceText(const std::string& path, const std::string& text);
|
||||||
|
|
||||||
void setText(std::string input); // Yeni kaynak kodu yükle
|
void setText(std::string input); // Yeni kaynak kodu yükle
|
||||||
void skipWhiteSpace(); // Boşluk/sekme/satırsonu karakterlerini atla
|
void skipWhiteSpace(); // Boşluk/sekme/satırsonu karakterlerini atla
|
||||||
bool isNumeric(); // Mevcut karakter 0-9 aralığında mı?
|
bool isNumeric(); // Mevcut karakter 0-9 aralığında mı?
|
||||||
INumber readNumeric(); // Sayı literal'ı oku ve INumber olarak döndür
|
INumber readNumeric(); // Sayı literal'ı oku ve INumber olarak döndür
|
||||||
};
|
};
|
||||||
|
|
||||||
// ============================================================================
|
|
||||||
// GERÇEKLEME (Implementation)
|
|
||||||
// ============================================================================
|
|
||||||
// Tüm metotlar inline olarak aşağıda tanımlanmıştır.
|
|
||||||
// Derleme modeli: header-only. main.cpp bu dosyayı include eder.
|
|
||||||
// ============================================================================
|
|
||||||
|
|
||||||
// --------------------------------------------------------------------------
|
|
||||||
// beginPosition: Mevcut offset'i yığına kaydet.
|
|
||||||
// İç içe çağrılabilir: 3 kere beginPosition → 3 elemanlı yığın.
|
|
||||||
// --------------------------------------------------------------------------
|
|
||||||
inline void Lexer::beginPosition() {
|
|
||||||
offsetMap.push_back(getLastPosition());
|
|
||||||
}
|
|
||||||
|
|
||||||
// --------------------------------------------------------------------------
|
|
||||||
// getLastPosition: Yığının tepesindeki konumu döndür.
|
|
||||||
// Yığın boşsa mevcut offset'i döndür (başlangıç durumu).
|
|
||||||
// --------------------------------------------------------------------------
|
|
||||||
inline int Lexer::getLastPosition() {
|
|
||||||
if (offsetMap.empty()) return offset;
|
|
||||||
return offsetMap.back();
|
|
||||||
}
|
|
||||||
|
|
||||||
// --------------------------------------------------------------------------
|
|
||||||
// acceptPosition: Yığındaki son geçici konumu kalıcı yap.
|
|
||||||
// Örnek: offsetMap=[5,10], offset=15 → offsetMap.back()=10 olur.
|
|
||||||
// Bu sayede include() denemesi başarılı olduğunda konum ilerletilmiş olur.
|
|
||||||
// --------------------------------------------------------------------------
|
|
||||||
inline void Lexer::acceptPosition() {
|
|
||||||
int t = offsetMap.back();
|
|
||||||
setLastPosition(t);
|
|
||||||
}
|
|
||||||
|
|
||||||
// --------------------------------------------------------------------------
|
|
||||||
// setLastPosition: Yığının tepesini veya offset'i değiştir.
|
|
||||||
// --------------------------------------------------------------------------
|
|
||||||
inline void Lexer::setLastPosition(int n) {
|
|
||||||
if (offsetMap.empty())
|
|
||||||
offset = n;
|
|
||||||
else
|
|
||||||
offsetMap.back() = n;
|
|
||||||
}
|
|
||||||
|
|
||||||
// --------------------------------------------------------------------------
|
|
||||||
// isEnd: Dosya sonuna gelindi mi? offset >= size.
|
|
||||||
// --------------------------------------------------------------------------
|
|
||||||
inline bool Lexer::isEnd() {
|
|
||||||
return size <= getOffset();
|
|
||||||
}
|
|
||||||
|
|
||||||
// --------------------------------------------------------------------------
|
|
||||||
// rejectPosition: Yığındaki son konumu at. Başarısız include() denemesi sonrası.
|
|
||||||
// --------------------------------------------------------------------------
|
|
||||||
inline void Lexer::rejectPosition() {
|
|
||||||
offsetMap.pop_back();
|
|
||||||
}
|
|
||||||
|
|
||||||
// --------------------------------------------------------------------------
|
|
||||||
// positionRange: Yığındaki en dış ve en iç konumu [start, end] olarak döndür.
|
|
||||||
// UYARI: new int[2] ile heap'te tahsis eder. Çağıran sorumludur.
|
|
||||||
// TODO: std::pair<int,int> veya yapı kullanarak tahsisi kaldır.
|
|
||||||
// --------------------------------------------------------------------------
|
|
||||||
inline int* Lexer::positionRange() {
|
|
||||||
int len = offsetMap.size();
|
|
||||||
if (len == 0)
|
|
||||||
return new int[2]{0, offset};
|
|
||||||
if (len == 1)
|
|
||||||
return new int[2]{offset, offsetMap[0]};
|
|
||||||
return new int[2]{offsetMap[len - 2], offsetMap[len - 1]};
|
|
||||||
}
|
|
||||||
|
|
||||||
// --------------------------------------------------------------------------
|
|
||||||
// getPositionRange: positionRange() aralığındaki metni string olarak döndür.
|
|
||||||
// --------------------------------------------------------------------------
|
|
||||||
inline std::string Lexer::getPositionRange() {
|
|
||||||
int* a = positionRange();
|
|
||||||
std::string mem;
|
|
||||||
for (int i = a[0]; i < a[1]; i++)
|
|
||||||
mem.push_back(input.at(i));
|
|
||||||
return mem;
|
|
||||||
}
|
|
||||||
|
|
||||||
// --------------------------------------------------------------------------
|
|
||||||
// include: Belirtilen kelime mevcut konumda başlıyor mu?
|
|
||||||
//
|
|
||||||
// Algoritma:
|
|
||||||
// 1. beginPosition() ile konumu kaydet
|
|
||||||
// 2. Kelimenin her karakterini sırayla karşılaştır
|
|
||||||
// 3. Eşleşmezse veya EOF olursa → rejectPosition() ve false dön
|
|
||||||
// 4. Tüm karakterler eşleşirse:
|
|
||||||
// - accept=true ise → acceptPosition() (konum kalıcı ilerler)
|
|
||||||
// - accept=false ise → rejectPosition() (konum eski haline döner)
|
|
||||||
// 5. true dön
|
|
||||||
//
|
|
||||||
// Neden accept parametresi var?
|
|
||||||
// Tokenizer scope() fonksiyonu, keyword'leri kontrol ederken accept=false
|
|
||||||
// kullanır. Çünkü bir keyword eşleşmesi, aynı zamanda daha uzun bir
|
|
||||||
// keyword'ün parçası olabilir (örn: "do", "double"ın başlangıcı).
|
|
||||||
// Eğer include("do", true) kullanılırsa, konum ilerler ve geri alınamaz.
|
|
||||||
// --------------------------------------------------------------------------
|
|
||||||
inline bool Lexer::include(std::string word, bool accept) {
|
|
||||||
beginPosition();
|
|
||||||
for (size_t i = 0; i < word.size(); i++) {
|
|
||||||
if (isEnd()) {
|
|
||||||
rejectPosition();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (word[i] != getchar()) {
|
|
||||||
rejectPosition();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
nextChar();
|
|
||||||
}
|
|
||||||
if (accept)
|
|
||||||
acceptPosition();
|
|
||||||
else
|
|
||||||
rejectPosition();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// --------------------------------------------------------------------------
|
|
||||||
// getOffset / setOffset: Konum erişimcileri.
|
|
||||||
// --------------------------------------------------------------------------
|
|
||||||
inline int Lexer::getOffset() {
|
|
||||||
return getLastPosition();
|
|
||||||
}
|
|
||||||
|
|
||||||
inline int Lexer::setOffset(int n) {
|
|
||||||
setLastPosition(n);
|
|
||||||
return getLastPosition();
|
|
||||||
}
|
|
||||||
|
|
||||||
// --------------------------------------------------------------------------
|
|
||||||
// getchar(additionalOffset): offset + ek kadar ilerideki karakteri oku.
|
|
||||||
// Sınır kontrolü yapar: target >= size ise '\0' döndürür ve hata mesajı basar.
|
|
||||||
// Bu metot özellikle keyword sınır kontrolünde kullanılır:
|
|
||||||
// "do" eşleşti, sıradaki karakter 'u' ise bu "double" olabilir → keyword değil
|
|
||||||
// --------------------------------------------------------------------------
|
|
||||||
inline char Lexer::getchar(int additionalOffset) {
|
|
||||||
int target = getOffset() + additionalOffset;
|
|
||||||
if (target >= size) {
|
|
||||||
std::cerr << "Lexer hatası: sınır aşımı\n";
|
|
||||||
return '\0';
|
|
||||||
}
|
|
||||||
return input.at(target);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline char Lexer::getchar() {
|
|
||||||
int target = getOffset();
|
|
||||||
if (target >= size) {
|
|
||||||
std::cerr << "Lexer hatası: sınır aşımı\n";
|
|
||||||
return '\0';
|
|
||||||
}
|
|
||||||
return input.at(target);
|
|
||||||
}
|
|
||||||
|
|
||||||
// --------------------------------------------------------------------------
|
|
||||||
// nextChar / toChar: Konum ilerletme.
|
|
||||||
// EOF kontrolü yapar — dosya sonundaysa ilerlemez.
|
|
||||||
// --------------------------------------------------------------------------
|
|
||||||
inline void Lexer::nextChar() {
|
|
||||||
if (!isEnd())
|
|
||||||
setOffset(getOffset() + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void Lexer::toChar(int n) {
|
|
||||||
if (!isEnd())
|
|
||||||
setOffset(getOffset() + n);
|
|
||||||
}
|
|
||||||
|
|
||||||
// --------------------------------------------------------------------------
|
|
||||||
// setText: Yeni kaynak kodu yükle. input ve size'ı günceller.
|
|
||||||
// --------------------------------------------------------------------------
|
|
||||||
inline void Lexer::setText(std::string text) {
|
|
||||||
input = text;
|
|
||||||
size = text.length();
|
|
||||||
}
|
|
||||||
|
|
||||||
// --------------------------------------------------------------------------
|
|
||||||
// skipWhiteSpace: Boşluk, sekme, satırsonu, satırbaşı karakterlerini atla.
|
|
||||||
// Yorum satırlarını atlamaz — bu Tokenizer'ın işi.
|
|
||||||
// --------------------------------------------------------------------------
|
|
||||||
inline void Lexer::skipWhiteSpace() {
|
|
||||||
while (!isEnd()) {
|
|
||||||
switch (getchar()) {
|
|
||||||
case '\r': // carriage return (Windows satırsonu \r\n)
|
|
||||||
case '\n': // line feed (Unix satırsonu)
|
|
||||||
case '\b': // backspace
|
|
||||||
case '\t': // tab
|
|
||||||
case ' ': // boşluk
|
|
||||||
nextChar();
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// --------------------------------------------------------------------------
|
|
||||||
// isNumeric: Mevcut karakter bir rakam mı? (0-9)
|
|
||||||
// Pointer aritmetiği veya ASCII tablosu karşılaştırması yerine basit aralık
|
|
||||||
// kontrolü. Performans: O(1), branchless (modern derleyiciler optimize eder).
|
|
||||||
// --------------------------------------------------------------------------
|
|
||||||
inline bool Lexer::isNumeric() {
|
|
||||||
char c = getchar();
|
|
||||||
return (c >= '0' && c <= '9');
|
|
||||||
}
|
|
||||||
|
|
||||||
// --------------------------------------------------------------------------
|
|
||||||
// readNumeric: Tam bir sayı literal'ı oku.
|
|
||||||
//
|
|
||||||
// Desteklenen formatlar (öncelik sırasıyla):
|
|
||||||
// 1. İşaret: -42, +3 (baştaki isteğe bağlı işaret)
|
|
||||||
// 2. 0x/0X: Hex (0xFF, 0X1A)
|
|
||||||
// 3. 0b/0B: Binary (0b1010)
|
|
||||||
// 4. 0 ile başlayan: Octal (0777) veya Float (0.5)
|
|
||||||
// 5. Ondalık: 42, 3.14
|
|
||||||
// 6. Bilimsel: 1e10, 2.5E-3, 1.0e+5
|
|
||||||
//
|
|
||||||
// Algoritma:
|
|
||||||
// 1. İsteğe bağlı işareti oku (+ veya -)
|
|
||||||
// 2. İlk karakter '0' ise → özel durum (hex/bin/octal/float kontrolü)
|
|
||||||
// 3. Ana döngü: rakamları, hex harflerini (a-f/A-F), nokta (.), epsilon (e/E) oku
|
|
||||||
// 4. Her karakterde taban uygunluğunu kontrol et (örn: octal'da 8-9 geçersiz)
|
|
||||||
// 5. İlk karakter '0' değilse → doğrudan decimal
|
|
||||||
//
|
|
||||||
// Özel durum: "0" takip eden karakter yoksa → tek haneli sayı, base=10.
|
|
||||||
// "0xFF" → hex, "0b10" → binary, "077" → octal, "0.5" → float.
|
|
||||||
//
|
|
||||||
// TODO: Hex float desteği (0x1.ffp10) — C99 standardı
|
|
||||||
// TODO: Sayısal ayraç: 1_000_000 — C++14/Java 7
|
|
||||||
// --------------------------------------------------------------------------
|
|
||||||
inline INumber Lexer::readNumeric() {
|
|
||||||
INumber num;
|
|
||||||
num.start = getLastPosition();
|
|
||||||
|
|
||||||
// --- Adım 1: İsteğe bağlı işaret ---
|
|
||||||
if (getchar() == '-') {
|
|
||||||
nextChar();
|
|
||||||
num.positive = false;
|
|
||||||
} else if (getchar() == '+') {
|
|
||||||
nextChar();
|
|
||||||
num.positive = true;
|
|
||||||
} else {
|
|
||||||
num.positive = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- Adım 2: İlk karakter '0' ise özel format kontrolü ---
|
|
||||||
bool nextDot = false;
|
|
||||||
if (getchar() == '0') {
|
|
||||||
num.token.push_back('0');
|
|
||||||
nextChar();
|
|
||||||
char c = getchar();
|
|
||||||
switch (c) {
|
|
||||||
case 'x': case 'X': // Hex: 0xFF, 0X1A
|
|
||||||
num.token.push_back(c);
|
|
||||||
num.base = 16;
|
|
||||||
nextChar();
|
|
||||||
break;
|
|
||||||
case 'b': case 'B': // Binary: 0b1010
|
|
||||||
num.token.push_back(c);
|
|
||||||
num.base = 2;
|
|
||||||
nextChar();
|
|
||||||
break;
|
|
||||||
case '.': // Float: 0.5, 0.0
|
|
||||||
num.token.push_back(c);
|
|
||||||
num.base = 10;
|
|
||||||
nextDot = true;
|
|
||||||
num.isFloat = true;
|
|
||||||
nextChar();
|
|
||||||
break;
|
|
||||||
case '0': case '1': case '2': case '3': case '4':
|
|
||||||
case '5': case '6': case '7':
|
|
||||||
// Octal: 0777 — sonraki karakter octal rakam ise devam et
|
|
||||||
num.base = 8;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
// Sadece "0" — takip eden karakter rakam değil.
|
|
||||||
// Hemen dön: base=10 (varsayılan).
|
|
||||||
// BUG FIX (commit 438bc0e): Eskiden bu dalda sıradaki karakter
|
|
||||||
// token'a ekleniyor ve base=8 yapılıyordu. Bu, "0;" durumunda
|
|
||||||
// ';' karakterinin sayıya eklenmesine neden oluyordu.
|
|
||||||
num.end = getLastPosition();
|
|
||||||
return num;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
num.base = 10;
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- Adım 3: Ana okuma döngüsü ---
|
|
||||||
// Bu döngü, geçerli tabana uygun tüm karakterleri okur.
|
|
||||||
// Her karakter tipi için taban uygunluğu kontrol edilir:
|
|
||||||
// - 0-1: tüm tabanlar
|
|
||||||
// - 2-7: base >= 8
|
|
||||||
// - 8-9: base >= 10
|
|
||||||
// - a-f/A-F: base >= 16
|
|
||||||
// - . (nokta): sadece ondalık, sadece bir kere
|
|
||||||
// - e/E: sadece ondalık ve hex (hex'te epsilon yok, direkt okunur)
|
|
||||||
while (!isEnd()) {
|
|
||||||
char c = getchar();
|
|
||||||
switch (c) {
|
|
||||||
case '0':
|
|
||||||
case '1':
|
|
||||||
num.token.push_back(c);
|
|
||||||
break;
|
|
||||||
case '2': case '3': case '4': case '5':
|
|
||||||
case '6': case '7':
|
|
||||||
if (num.base >= 8)
|
|
||||||
num.token.push_back(c);
|
|
||||||
else {
|
|
||||||
num.end = getLastPosition();
|
|
||||||
return num;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case '8': case '9':
|
|
||||||
if (num.base >= 10)
|
|
||||||
num.token.push_back(c);
|
|
||||||
else {
|
|
||||||
num.end = getLastPosition();
|
|
||||||
return num;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'a': case 'A': case 'b': case 'B':
|
|
||||||
case 'c': case 'C': case 'd': case 'D':
|
|
||||||
case 'f': case 'F':
|
|
||||||
if (num.base >= 16)
|
|
||||||
num.token.push_back(c);
|
|
||||||
else {
|
|
||||||
num.end = getLastPosition();
|
|
||||||
return num;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case '.':
|
|
||||||
// Nokta: Sadece bir kere izin verilir.
|
|
||||||
// .5 gibi başıboş noktalı sayılar için "0." öneki eklenir.
|
|
||||||
if (!nextDot) {
|
|
||||||
if (num.token.empty())
|
|
||||||
num.token += "0.";
|
|
||||||
else
|
|
||||||
num.token.push_back('.');
|
|
||||||
nextDot = true;
|
|
||||||
num.isFloat = true;
|
|
||||||
} else {
|
|
||||||
// İkinci nokta → sayı bitti
|
|
||||||
num.end = getLastPosition();
|
|
||||||
return num;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'e': case 'E':
|
|
||||||
// Epsilon (bilimsel gösterim):
|
|
||||||
// - Hex tabanda: epsilon DEĞİL, hex hanesi olarak okunur.
|
|
||||||
// - Decimal tabanda: 1e10, 2.5E-3 formatı.
|
|
||||||
if (num.base == 16) {
|
|
||||||
num.token.push_back(c);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (num.base == 10) {
|
|
||||||
num.hasEpsilon = true;
|
|
||||||
num.token.push_back(c);
|
|
||||||
nextChar();
|
|
||||||
c = getchar();
|
|
||||||
// İsteğe bağlı işaret: e+10, E-3
|
|
||||||
if (c == '+' || c == '-') {
|
|
||||||
num.token.push_back(c);
|
|
||||||
nextChar();
|
|
||||||
}
|
|
||||||
// Epsilon sonrası rakamları oku
|
|
||||||
while (!isEnd()) {
|
|
||||||
c = getchar();
|
|
||||||
if (c >= '0' && c <= '9') {
|
|
||||||
num.token.push_back(c);
|
|
||||||
nextChar();
|
|
||||||
} else {
|
|
||||||
num.end = getLastPosition();
|
|
||||||
return num;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
num.end = getLastPosition();
|
|
||||||
return num;
|
|
||||||
default:
|
|
||||||
// Tanınmayan karakter → sayı bitti
|
|
||||||
num.end = getLastPosition();
|
|
||||||
return num;
|
|
||||||
}
|
|
||||||
nextChar();
|
|
||||||
}
|
|
||||||
num.end = getLastPosition();
|
|
||||||
return num;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // SAQUT_LEXER
|
#endif // SAQUT_LEXER
|
||||||
|
|
|
||||||
184
src/main.cpp
184
src/main.cpp
|
|
@ -3,118 +3,94 @@
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
//
|
//
|
||||||
// DİZİN: src/main.cpp
|
// DİZİN: src/main.cpp
|
||||||
// KATMAN: En üst — tüm alt katmanları birleştirir
|
// KATMAN: En üst — CLI dispatcher'ı başlatır
|
||||||
// BAĞIMLI: Tokenizer, Parser, IR (ve dolaylı olarak Lexer, AST, Token)
|
|
||||||
//
|
|
||||||
// AMAÇ:
|
|
||||||
// Derleyici pipeline'ını başlatır:
|
|
||||||
// 1. source.sqt dosyasını oku
|
|
||||||
// 2. Lexing + Tokenizing
|
|
||||||
// 3. Parsing (AST üretimi)
|
|
||||||
// 4. IR üretimi
|
|
||||||
// 5. Sonuçları konsola yazdır (debug modu)
|
|
||||||
//
|
//
|
||||||
// KULLANIM:
|
// KULLANIM:
|
||||||
// ./saqut → source.sqt dosyasını derler
|
// saqut → yardım
|
||||||
// echo "1+2" > source.sqt && ./saqut → hızlı test
|
// saqut run <dosya> → pipeline debug çıktısı
|
||||||
|
// saqut tokens <dosya> → token listesi
|
||||||
|
// saqut ast <dosya> [-o çıktı] → JSON AST + analiz
|
||||||
|
// saqut symbols <dosya> → sembol tablosu
|
||||||
|
// saqut - → stdin modu (TODO)
|
||||||
//
|
//
|
||||||
// GELECEK:
|
// YENİ KOMUT EKLEMEK İÇİN:
|
||||||
// - Komut satırı argümanları: ./saqut file.sqt -o output
|
// 1. src/cli/commands/x.hpp oluştur
|
||||||
// - Mod seçimi: ./saqut --mode=parse|ir|compile|run
|
// 2. Bu dosyada #include et
|
||||||
// - Birden fazla dosya: ./saqut file1.sqt file2.sqt
|
// 3. cli.registerCommand({...}) ile kaydet
|
||||||
//
|
//
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <fstream>
|
#include "cli/args.hpp"
|
||||||
#include <sstream>
|
#include "cli/cli.hpp"
|
||||||
#include <string>
|
#include "cli/commands/run.hpp"
|
||||||
#include "tokenizer/tokenizer.hpp"
|
#include "cli/commands/tokens.hpp"
|
||||||
#include "parser/parser.hpp"
|
#include "cli/commands/ast.hpp"
|
||||||
#include "ir/ir.hpp"
|
#include "cli/commands/symbols.hpp"
|
||||||
|
#include "cli/commands/check.hpp"
|
||||||
|
#include "cli/commands/ir.hpp"
|
||||||
|
|
||||||
int main() {
|
int main(int argc, char* argv[]) {
|
||||||
// ------------------------------------------------------------------
|
// Komutları kaydet
|
||||||
// 1. Kaynak dosyayı oku
|
CliDispatcher cli;
|
||||||
// ------------------------------------------------------------------
|
|
||||||
// Şimdilik sabit dosya adı: source.sqt.
|
cli.registerCommand({"run",
|
||||||
// TODO: argc/argv ile dosya adı al.
|
"Pipeline'ı çalıştır (token → AST → IR)",
|
||||||
// ------------------------------------------------------------------
|
false, cmdRun});
|
||||||
std::ifstream file("source.sqt", std::ios::in | std::ios::binary);
|
|
||||||
if (!file.is_open()) {
|
cli.registerCommand({"tokens",
|
||||||
std::cerr << "Hata: source.sqt dosyası açılamadı\n";
|
"Token listesini göster",
|
||||||
return 1;
|
false, cmdTokens});
|
||||||
|
|
||||||
|
cli.registerCommand({"ast",
|
||||||
|
"JSON formatında AST hiyerarşisi ve analiz",
|
||||||
|
false, cmdAst});
|
||||||
|
|
||||||
|
cli.registerCommand({"symbols",
|
||||||
|
"Sembol tablosu (fonksiyonlar, değişkenler)",
|
||||||
|
false, cmdSymbols});
|
||||||
|
|
||||||
|
cli.registerCommand({"check",
|
||||||
|
"Semantik analiz — tip denetimi + yapısal doğrulama",
|
||||||
|
false, cmdCheck});
|
||||||
|
|
||||||
|
cli.registerCommand({"ir",
|
||||||
|
"IR talimat listesini göster (ara temsil — bytecode öncesi)",
|
||||||
|
false, cmdIr});
|
||||||
|
|
||||||
|
// --- Gelecek komutlar (TODO) ---
|
||||||
|
cli.registerCommand({"compile",
|
||||||
|
"TODO: Kaynak kodu derle",
|
||||||
|
false, [](const CliArgs&) {
|
||||||
|
std::cerr << "TODO: compile komutu henüz eklenmedi\n"; return 1;
|
||||||
|
}});
|
||||||
|
|
||||||
|
cli.registerCommand({"parse",
|
||||||
|
"TODO: IR üret",
|
||||||
|
false, [](const CliArgs&) {
|
||||||
|
std::cerr << "TODO: parse komutu henüz eklenmedi\n"; return 1;
|
||||||
|
}});
|
||||||
|
|
||||||
|
cli.registerCommand({"transpile",
|
||||||
|
"TODO: C koduna çevir",
|
||||||
|
false, [](const CliArgs&) {
|
||||||
|
std::cerr << "TODO: transpile komutu henüz eklenmedi\n"; return 1;
|
||||||
|
}});
|
||||||
|
|
||||||
|
cli.registerCommand({"interpret",
|
||||||
|
"TODO: Interpreter modu",
|
||||||
|
true, [](const CliArgs&) {
|
||||||
|
std::cerr << "TODO: interpret komutu henüz eklenmedi\n"; return 1;
|
||||||
|
}});
|
||||||
|
|
||||||
|
// Argümanları ayrıştır
|
||||||
|
CliArgs args = parseArgs(argc, argv);
|
||||||
|
|
||||||
|
// Argümansız çağrı → help
|
||||||
|
if (argc <= 1) {
|
||||||
|
cli.printHelp();
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::stringstream buffer;
|
return cli.dispatch(args);
|
||||||
buffer << file.rdbuf();
|
|
||||||
std::string source = buffer.str();
|
|
||||||
file.close();
|
|
||||||
|
|
||||||
std::cout << "=== saQut Compiler ===\n";
|
|
||||||
std::cout << "Kaynak kod:\n" << source << "\n\n";
|
|
||||||
|
|
||||||
// ------------------------------------------------------------------
|
|
||||||
// 2. Lexing → Tokenizing
|
|
||||||
// ------------------------------------------------------------------
|
|
||||||
// Tokenizer, Lexer'ı içerir. scan() tüm pipeline'ı çalıştırır.
|
|
||||||
// Token'lar heap'te new ile oluşturulur, iş bitince silinmeli.
|
|
||||||
// ------------------------------------------------------------------
|
|
||||||
Tokenizer tokenizer;
|
|
||||||
auto tokens = tokenizer.scan(source);
|
|
||||||
|
|
||||||
std::cout << "Tokenler (" << tokens.size() << " adet):\n";
|
|
||||||
for (auto* t : tokens) {
|
|
||||||
std::cout << " [" << t->gettype() << "] \"" << t->token << "\"\n";
|
|
||||||
}
|
|
||||||
std::cout << "\n";
|
|
||||||
|
|
||||||
// ------------------------------------------------------------------
|
|
||||||
// 3. Parsing → AST
|
|
||||||
// ------------------------------------------------------------------
|
|
||||||
// Parser, token listesini alır, AST üretir.
|
|
||||||
// parse() artık parseProgram()'ı çağırır — birden fazla deklarasyon
|
|
||||||
// veya statement içeren tam programları ayrıştırabilir.
|
|
||||||
// ------------------------------------------------------------------
|
|
||||||
Parser parser;
|
|
||||||
ASTNode* ast = parser.parse(tokens);
|
|
||||||
|
|
||||||
if (ast) {
|
|
||||||
std::cout << "AST:\n";
|
|
||||||
ast->log(0); // Ağacı girintili olarak yazdır
|
|
||||||
std::cout << "\n";
|
|
||||||
|
|
||||||
// ------------------------------------------------------------------
|
|
||||||
// 4. IR Üretimi
|
|
||||||
// ------------------------------------------------------------------
|
|
||||||
// CodeGenerator AST'yi dolaşır, sanal register makine komutları üretir.
|
|
||||||
// Şu anda sadece matematik işlemleri ve literal'lar destekleniyor.
|
|
||||||
// ------------------------------------------------------------------
|
|
||||||
CodeGenerator cg;
|
|
||||||
cg.parse(ast);
|
|
||||||
std::cout << "IR (" << cg.IROpDatas.size() << " komut):\n";
|
|
||||||
for (size_t i = 0; i < cg.IROpDatas.size(); i++) {
|
|
||||||
auto& op = cg.IROpDatas[i];
|
|
||||||
std::cout << " [" << i << "] reg" << op.targetReg << " = ";
|
|
||||||
switch (op.op) {
|
|
||||||
case OPCode::mathadd: std::cout << "add"; break;
|
|
||||||
case OPCode::mathsub: std::cout << "sub"; break;
|
|
||||||
case OPCode::mathmul: std::cout << "mul"; break;
|
|
||||||
case OPCode::mathdiv: std::cout << "div"; break;
|
|
||||||
case OPCode::declare: std::cout << "literal"; break;
|
|
||||||
}
|
|
||||||
// arg1.value.index(): 0=int, 1=float
|
|
||||||
std::cout << " (" << op.arg1.value.index() << ")\n";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ------------------------------------------------------------------
|
|
||||||
// 5. Temizlik
|
|
||||||
// ------------------------------------------------------------------
|
|
||||||
// Token'lar heap'te oluşturuldu, manuel silinmeli.
|
|
||||||
// TODO: std::unique_ptr ile otomatik bellek yönetimi.
|
|
||||||
// ------------------------------------------------------------------
|
|
||||||
for (auto* t : tokens) delete t;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,286 @@
|
||||||
|
// ============================================================================
|
||||||
|
// saQut — AST Derin Klonlama (ADR-007)
|
||||||
|
//
|
||||||
|
// deepClone(node): tüm ağacı kopyalar, parent pointer'ları yeniden bağlar.
|
||||||
|
// IdentifierNode::resolvedSymbol orijinal sembol tablosunu gösterir (read-only,
|
||||||
|
// optimizasyon için yeterli — ADR-007).
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
#ifndef SAQUT_OPT_AST_CLONE
|
||||||
|
#define SAQUT_OPT_AST_CLONE
|
||||||
|
|
||||||
|
#include "parser/ast_node.hpp"
|
||||||
|
#include "parser/nodes/program.hpp"
|
||||||
|
#include "parser/nodes/declarations.hpp"
|
||||||
|
#include "parser/nodes/statements.hpp"
|
||||||
|
#include "parser/nodes/expressions.hpp"
|
||||||
|
#include "parser/nodes/binary_expr.hpp"
|
||||||
|
#include "parser/nodes/literal.hpp"
|
||||||
|
#include "parser/nodes/identifier.hpp"
|
||||||
|
|
||||||
|
inline ASTNode* deepClone(ASTNode* node);
|
||||||
|
|
||||||
|
// ── Yardımcı: typed pointer klonla ve parent'ı bağla ────────────────────────
|
||||||
|
static inline ASTNode* cloneChild(ASTNode* child, ASTNode* newParent) {
|
||||||
|
if (!child) return nullptr;
|
||||||
|
ASTNode* c = deepClone(child);
|
||||||
|
c->parent = newParent;
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline ASTNode* deepClone(ASTNode* node) {
|
||||||
|
if (!node) return nullptr;
|
||||||
|
|
||||||
|
switch (node->kind) {
|
||||||
|
|
||||||
|
// ── ProgramNode ──────────────────────────────────────────────────────────
|
||||||
|
case ASTKind::Program: {
|
||||||
|
auto* src = static_cast<ProgramNode*>(node);
|
||||||
|
auto* dst = new ProgramNode();
|
||||||
|
dst->loc = src->loc;
|
||||||
|
for (auto* ch : src->getChildren()) dst->addChild(deepClone(ch));
|
||||||
|
return dst;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── FunctionDeclNode ─────────────────────────────────────────────────────
|
||||||
|
case ASTKind::FunctionDecl: {
|
||||||
|
auto* src = static_cast<FunctionDeclNode*>(node);
|
||||||
|
auto* dst = new FunctionDeclNode();
|
||||||
|
dst->loc = src->loc;
|
||||||
|
dst->name = src->name;
|
||||||
|
dst->returnType = src->returnType;
|
||||||
|
for (auto* p : src->params) {
|
||||||
|
auto* cp = static_cast<VariableDeclNode*>(deepClone(p));
|
||||||
|
cp->parent = dst;
|
||||||
|
dst->params.push_back(cp);
|
||||||
|
}
|
||||||
|
for (auto* ch : src->getChildren()) dst->addChild(deepClone(ch));
|
||||||
|
return dst;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── VariableDeclNode ─────────────────────────────────────────────────────
|
||||||
|
case ASTKind::VariableDecl: {
|
||||||
|
auto* src = static_cast<VariableDeclNode*>(node);
|
||||||
|
auto* dst = new VariableDeclNode();
|
||||||
|
dst->loc = src->loc;
|
||||||
|
dst->name = src->name;
|
||||||
|
dst->varType = src->varType;
|
||||||
|
dst->isReachable = src->isReachable;
|
||||||
|
if (src->initExpr) dst->initExpr = cloneChild(src->initExpr, dst);
|
||||||
|
for (auto* ch : src->getChildren()) dst->addChild(deepClone(ch));
|
||||||
|
return dst;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── StructDeclNode ───────────────────────────────────────────────────────
|
||||||
|
case ASTKind::StructDecl: {
|
||||||
|
auto* src = static_cast<StructDeclNode*>(node);
|
||||||
|
auto* dst = new StructDeclNode();
|
||||||
|
dst->loc = src->loc;
|
||||||
|
dst->name = src->name;
|
||||||
|
for (auto* ch : src->getChildren()) dst->addChild(deepClone(ch));
|
||||||
|
return dst;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── BlockNode ────────────────────────────────────────────────────────────
|
||||||
|
case ASTKind::Block: {
|
||||||
|
auto* src = static_cast<BlockNode*>(node);
|
||||||
|
auto* dst = new BlockNode();
|
||||||
|
dst->loc = src->loc;
|
||||||
|
dst->isReachable = src->isReachable;
|
||||||
|
for (auto* ch : src->getChildren()) dst->addChild(deepClone(ch));
|
||||||
|
return dst;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── IfStatementNode ──────────────────────────────────────────────────────
|
||||||
|
case ASTKind::IfStatement: {
|
||||||
|
auto* src = static_cast<IfStatementNode*>(node);
|
||||||
|
auto* dst = new IfStatementNode();
|
||||||
|
dst->loc = src->loc;
|
||||||
|
dst->isReachable = src->isReachable;
|
||||||
|
dst->condition = cloneChild(src->condition, dst);
|
||||||
|
dst->thenBranch = cloneChild(src->thenBranch, dst);
|
||||||
|
dst->elseBranch = cloneChild(src->elseBranch, dst);
|
||||||
|
return dst;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── WhileStatementNode ───────────────────────────────────────────────────
|
||||||
|
case ASTKind::WhileStatement: {
|
||||||
|
auto* src = static_cast<WhileStatementNode*>(node);
|
||||||
|
auto* dst = new WhileStatementNode();
|
||||||
|
dst->loc = src->loc;
|
||||||
|
dst->isReachable = src->isReachable;
|
||||||
|
dst->condition = cloneChild(src->condition, dst);
|
||||||
|
dst->body = cloneChild(src->body, dst);
|
||||||
|
return dst;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── ForStatementNode ─────────────────────────────────────────────────────
|
||||||
|
case ASTKind::ForStatement: {
|
||||||
|
auto* src = static_cast<ForStatementNode*>(node);
|
||||||
|
auto* dst = new ForStatementNode();
|
||||||
|
dst->loc = src->loc;
|
||||||
|
dst->isReachable = src->isReachable;
|
||||||
|
dst->init = cloneChild(src->init, dst);
|
||||||
|
dst->condition = cloneChild(src->condition, dst);
|
||||||
|
dst->update = cloneChild(src->update, dst);
|
||||||
|
dst->body = cloneChild(src->body, dst);
|
||||||
|
return dst;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── DoWhileStatementNode ─────────────────────────────────────────────────
|
||||||
|
case ASTKind::DoWhileStatement: {
|
||||||
|
auto* src = static_cast<DoWhileStatementNode*>(node);
|
||||||
|
auto* dst = new DoWhileStatementNode();
|
||||||
|
dst->loc = src->loc;
|
||||||
|
dst->isReachable = src->isReachable;
|
||||||
|
dst->body = cloneChild(src->body, dst);
|
||||||
|
dst->condition = cloneChild(src->condition, dst);
|
||||||
|
return dst;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── ReturnStatementNode ──────────────────────────────────────────────────
|
||||||
|
case ASTKind::ReturnStatement: {
|
||||||
|
auto* src = static_cast<ReturnStatementNode*>(node);
|
||||||
|
auto* dst = new ReturnStatementNode();
|
||||||
|
dst->loc = src->loc;
|
||||||
|
dst->isReachable = src->isReachable;
|
||||||
|
dst->value = cloneChild(src->value, dst);
|
||||||
|
return dst;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── BreakStatementNode ───────────────────────────────────────────────────
|
||||||
|
case ASTKind::BreakStatement: {
|
||||||
|
auto* src = static_cast<BreakStatementNode*>(node);
|
||||||
|
auto* dst = new BreakStatementNode();
|
||||||
|
dst->loc = src->loc;
|
||||||
|
dst->isReachable = src->isReachable;
|
||||||
|
return dst;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── ContinueStatementNode ────────────────────────────────────────────────
|
||||||
|
case ASTKind::ContinueStatement: {
|
||||||
|
auto* src = static_cast<ContinueStatementNode*>(node);
|
||||||
|
auto* dst = new ContinueStatementNode();
|
||||||
|
dst->loc = src->loc;
|
||||||
|
dst->isReachable = src->isReachable;
|
||||||
|
return dst;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── ExpressionStatementNode ──────────────────────────────────────────────
|
||||||
|
case ASTKind::ExpressionStatement: {
|
||||||
|
auto* src = static_cast<ExpressionStatementNode*>(node);
|
||||||
|
auto* dst = new ExpressionStatementNode();
|
||||||
|
dst->loc = src->loc;
|
||||||
|
dst->isReachable = src->isReachable;
|
||||||
|
dst->expression = cloneChild(src->expression, dst);
|
||||||
|
return dst;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── BinaryExpressionNode ─────────────────────────────────────────────────
|
||||||
|
case ASTKind::BinaryExpression: {
|
||||||
|
auto* src = static_cast<BinaryExpressionNode*>(node);
|
||||||
|
auto* dst = new BinaryExpressionNode();
|
||||||
|
dst->loc = src->loc;
|
||||||
|
dst->Operator = src->Operator;
|
||||||
|
dst->resolvedType = src->resolvedType;
|
||||||
|
dst->isConstant = src->isConstant;
|
||||||
|
dst->Left = cloneChild(src->Left, dst);
|
||||||
|
dst->Right = cloneChild(src->Right, dst);
|
||||||
|
return dst;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── LiteralNode ──────────────────────────────────────────────────────────
|
||||||
|
case ASTKind::Literal: {
|
||||||
|
auto* src = static_cast<LiteralNode*>(node);
|
||||||
|
auto* dst = new LiteralNode();
|
||||||
|
dst->loc = src->loc;
|
||||||
|
dst->literalType = src->literalType;
|
||||||
|
dst->literalBase = src->literalBase;
|
||||||
|
dst->isFloatValue = src->isFloatValue;
|
||||||
|
dst->lexerToken = src->lexerToken; // orijinal token, salt-okunur
|
||||||
|
dst->parserToken = src->parserToken; // aynı token pointer, salt-okunur
|
||||||
|
dst->resolvedType = src->resolvedType;
|
||||||
|
dst->isConstant = src->isConstant;
|
||||||
|
dst->hasDirectValue = src->hasDirectValue;
|
||||||
|
dst->directIntValue = src->directIntValue;
|
||||||
|
return dst;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── IdentifierNode ───────────────────────────────────────────────────────
|
||||||
|
case ASTKind::Identifier: {
|
||||||
|
auto* src = static_cast<IdentifierNode*>(node);
|
||||||
|
auto* dst = new IdentifierNode();
|
||||||
|
dst->loc = src->loc;
|
||||||
|
dst->lexerToken = src->lexerToken;
|
||||||
|
dst->parserToken = src->parserToken;
|
||||||
|
dst->resolvedSymbol = src->resolvedSymbol; // orijinal tablo, salt-okunur
|
||||||
|
dst->resolvedType = src->resolvedType;
|
||||||
|
dst->isConstant = src->isConstant;
|
||||||
|
return dst;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── PostfixNode ──────────────────────────────────────────────────────────
|
||||||
|
case ASTKind::Postfix: {
|
||||||
|
auto* src = static_cast<PostfixNode*>(node);
|
||||||
|
auto* dst = new PostfixNode();
|
||||||
|
dst->loc = src->loc;
|
||||||
|
dst->Operator = src->Operator;
|
||||||
|
dst->resolvedType = src->resolvedType;
|
||||||
|
dst->isConstant = src->isConstant;
|
||||||
|
dst->operand = cloneChild(src->operand, dst);
|
||||||
|
return dst;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── CallExpressionNode ───────────────────────────────────────────────────
|
||||||
|
case ASTKind::Call: {
|
||||||
|
auto* src = static_cast<CallExpressionNode*>(node);
|
||||||
|
auto* dst = new CallExpressionNode();
|
||||||
|
dst->loc = src->loc;
|
||||||
|
dst->resolvedType = src->resolvedType;
|
||||||
|
dst->isConstant = src->isConstant;
|
||||||
|
dst->callee = cloneChild(src->callee, dst);
|
||||||
|
for (auto* arg : src->arguments) {
|
||||||
|
ASTNode* ca = deepClone(arg);
|
||||||
|
ca->parent = dst;
|
||||||
|
dst->arguments.push_back(ca);
|
||||||
|
}
|
||||||
|
return dst;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── MemberAccessNode ─────────────────────────────────────────────────────
|
||||||
|
case ASTKind::MemberAccess: {
|
||||||
|
auto* src = static_cast<MemberAccessNode*>(node);
|
||||||
|
auto* dst = new MemberAccessNode();
|
||||||
|
dst->loc = src->loc;
|
||||||
|
dst->resolvedType = src->resolvedType;
|
||||||
|
dst->isConstant = src->isConstant;
|
||||||
|
dst->member = src->member;
|
||||||
|
dst->arrow = src->arrow;
|
||||||
|
dst->object = cloneChild(src->object, dst);
|
||||||
|
return dst;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── IndexExpressionNode ──────────────────────────────────────────────────
|
||||||
|
case ASTKind::IndexExpression: {
|
||||||
|
auto* src = static_cast<IndexExpressionNode*>(node);
|
||||||
|
auto* dst = new IndexExpressionNode();
|
||||||
|
dst->loc = src->loc;
|
||||||
|
dst->resolvedType = src->resolvedType;
|
||||||
|
dst->isConstant = src->isConstant;
|
||||||
|
dst->object = cloneChild(src->object, dst);
|
||||||
|
dst->index = cloneChild(src->index, dst);
|
||||||
|
return dst;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── UnaryExpression ──────────────────────────────────────────────────────
|
||||||
|
// UnaryExpression şu anda ayrı bir sınıf değil; parser tarafından
|
||||||
|
// BinaryExpression veya PostfixNode olarak temsil ediliyor.
|
||||||
|
// Gelecekte eklenmesi gerekirse buraya eklenecek.
|
||||||
|
default:
|
||||||
|
// Bilinmeyen node tipi — klonlanamaz; orijinal döndür (güvenli değil ama
|
||||||
|
// derleme aşamasında tüm tipler yukarıda kaplanmalı).
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // SAQUT_OPT_AST_CLONE
|
||||||
|
|
@ -0,0 +1,363 @@
|
||||||
|
// ============================================================================
|
||||||
|
// saQut — Sabit Katlama (Constant Folding) Pass (ADR-009)
|
||||||
|
//
|
||||||
|
// BinaryExpression(Literal, OP, Literal) → Literal
|
||||||
|
// Sadece tam sayı sabitleri katlanır; kayan nokta şimdilik dışarıda.
|
||||||
|
// Derleme zamanı sıfıra bölme: W002 uyarısı verilir, katlama yapılmaz.
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
#ifndef SAQUT_OPT_CONSTANT_FOLDING
|
||||||
|
#define SAQUT_OPT_CONSTANT_FOLDING
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include "opt/optimization_pass.hpp"
|
||||||
|
#include "parser/nodes/binary_expr.hpp"
|
||||||
|
#include "parser/nodes/literal.hpp"
|
||||||
|
#include "parser/nodes/identifier.hpp"
|
||||||
|
#include "parser/nodes/statements.hpp"
|
||||||
|
#include "parser/nodes/expressions.hpp"
|
||||||
|
#include "parser/nodes/declarations.hpp"
|
||||||
|
#include "parser/nodes/program.hpp"
|
||||||
|
#include "diagnostic/diagnostic_engine.hpp"
|
||||||
|
|
||||||
|
class ConstantFoldingPass : public OptimizationPass {
|
||||||
|
public:
|
||||||
|
explicit ConstantFoldingPass(DiagnosticEngine& diag) : diag_(diag) {}
|
||||||
|
|
||||||
|
bool run(ASTNode* root, SymbolTable*) override {
|
||||||
|
changed_ = false;
|
||||||
|
fold(root);
|
||||||
|
return changed_;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string& name() const override {
|
||||||
|
static std::string n = "constant-folding";
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
DiagnosticEngine& diag_;
|
||||||
|
bool changed_ = false;
|
||||||
|
|
||||||
|
// ── Literal değer okuma ──────────────────────────────────────────────────
|
||||||
|
static bool isIntLit(ASTNode* node) {
|
||||||
|
auto* lit = dynamic_cast<LiteralNode*>(node);
|
||||||
|
return lit && lit->literalType == LiteralType::INTEGER;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Boolean veya integer literal — unary ! için her ikisi de geçerli
|
||||||
|
static bool isScalarLit(ASTNode* node) {
|
||||||
|
auto* lit = dynamic_cast<LiteralNode*>(node);
|
||||||
|
return lit && (lit->literalType == LiteralType::INTEGER ||
|
||||||
|
lit->literalType == LiteralType::BOOLEAN);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int getIntVal(LiteralNode* lit) {
|
||||||
|
if (lit->hasDirectValue) return lit->directIntValue;
|
||||||
|
if (lit->parserToken.token) return std::stoi(lit->parserToken.token->token);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Boolean literali int'e çevir: "true"→1, "false"→0
|
||||||
|
static int getScalarVal(LiteralNode* lit) {
|
||||||
|
if (lit->literalType == LiteralType::BOOLEAN) {
|
||||||
|
if (!lit->parserToken.token) return 0;
|
||||||
|
return (lit->parserToken.token->token == "true") ? 1 : 0;
|
||||||
|
}
|
||||||
|
return getIntVal(lit);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unary operatör katlama
|
||||||
|
static bool canFoldUnary(TokenType op) {
|
||||||
|
switch (op) {
|
||||||
|
case TokenType::BANG: // !
|
||||||
|
case TokenType::TILDE: // ~
|
||||||
|
case TokenType::MINUS: // - (negatif)
|
||||||
|
case TokenType::PLUS: // + (pozitif, no-op)
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int computeUnary(TokenType op, int v) {
|
||||||
|
switch (op) {
|
||||||
|
case TokenType::BANG: return !v ? 1 : 0;
|
||||||
|
case TokenType::TILDE: return ~v;
|
||||||
|
case TokenType::MINUS: return -v;
|
||||||
|
case TokenType::PLUS: return v;
|
||||||
|
default: return v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Operatör hesaplama ───────────────────────────────────────────────────
|
||||||
|
static bool canFoldOp(TokenType op) {
|
||||||
|
switch (op) {
|
||||||
|
case TokenType::PLUS:
|
||||||
|
case TokenType::MINUS:
|
||||||
|
case TokenType::STAR:
|
||||||
|
case TokenType::SLASH:
|
||||||
|
case TokenType::PERCENT:
|
||||||
|
case TokenType::EQUAL_EQUAL:
|
||||||
|
case TokenType::BANG_EQUAL:
|
||||||
|
case TokenType::LESS:
|
||||||
|
case TokenType::GREATER:
|
||||||
|
case TokenType::LESS_EQUAL:
|
||||||
|
case TokenType::GREATER_EQUAL:
|
||||||
|
case TokenType::AMPERSAND_AMPERSAND:
|
||||||
|
case TokenType::PIPE_PIPE:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int computeOp(TokenType op, int l, int r) {
|
||||||
|
switch (op) {
|
||||||
|
case TokenType::PLUS: return l + r;
|
||||||
|
case TokenType::MINUS: return l - r;
|
||||||
|
case TokenType::STAR: return l * r;
|
||||||
|
case TokenType::SLASH: return l / r;
|
||||||
|
case TokenType::PERCENT: return l % r;
|
||||||
|
case TokenType::EQUAL_EQUAL: return l == r ? 1 : 0;
|
||||||
|
case TokenType::BANG_EQUAL: return l != r ? 1 : 0;
|
||||||
|
case TokenType::LESS: return l < r ? 1 : 0;
|
||||||
|
case TokenType::GREATER: return l > r ? 1 : 0;
|
||||||
|
case TokenType::LESS_EQUAL: return l <= r ? 1 : 0;
|
||||||
|
case TokenType::GREATER_EQUAL: return l >= r ? 1 : 0;
|
||||||
|
case TokenType::AMPERSAND_AMPERSAND: return (l && r) ? 1 : 0;
|
||||||
|
case TokenType::PIPE_PIPE: return (l || r) ? 1 : 0;
|
||||||
|
default: return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Sentetik literal oluştur ─────────────────────────────────────────────
|
||||||
|
static LiteralNode* makeFoldedLit(int value, const SourceLocation& loc, const Type& type) {
|
||||||
|
auto* lit = new LiteralNode();
|
||||||
|
lit->loc = loc;
|
||||||
|
lit->literalType = LiteralType::INTEGER;
|
||||||
|
lit->hasDirectValue = true;
|
||||||
|
lit->directIntValue = value;
|
||||||
|
lit->resolvedType = type;
|
||||||
|
lit->isConstant = true;
|
||||||
|
return lit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Ana ziyaretçi: bottom-up, pointer-by-reference ───────────────────────
|
||||||
|
// Dönüş değeri: aynı node veya yerine geçen yeni node.
|
||||||
|
// Folding yapıldıysa orijinal node silinir, yeni node döndürülür.
|
||||||
|
ASTNode* fold(ASTNode* node) {
|
||||||
|
if (!node) return nullptr;
|
||||||
|
|
||||||
|
switch (node->kind) {
|
||||||
|
|
||||||
|
// ── İfadeler ────────────────────────────────────────────────────────
|
||||||
|
case ASTKind::BinaryExpression: {
|
||||||
|
auto* bin = static_cast<BinaryExpressionNode*>(node);
|
||||||
|
|
||||||
|
// Alt ağaçları önce kat
|
||||||
|
ASTNode* newLeft = fold(bin->Left);
|
||||||
|
if (newLeft != bin->Left) { bin->Left = newLeft; if (newLeft) newLeft->parent = bin; }
|
||||||
|
|
||||||
|
ASTNode* newRight = fold(bin->Right);
|
||||||
|
if (newRight != bin->Right) { bin->Right = newRight; if (newRight) newRight->parent = bin; }
|
||||||
|
|
||||||
|
// ── Unary dal: Left==nullptr → !x, ~x, -x, +x ──────────────────
|
||||||
|
if (bin->Left == nullptr) {
|
||||||
|
if (!isScalarLit(bin->Right)) return bin;
|
||||||
|
if (!canFoldUnary(bin->Operator)) return bin;
|
||||||
|
|
||||||
|
auto* rlit = static_cast<LiteralNode*>(bin->Right);
|
||||||
|
int rv = getScalarVal(rlit);
|
||||||
|
int result = computeUnary(bin->Operator, rv);
|
||||||
|
|
||||||
|
LiteralNode* lit = makeFoldedLit(result, bin->loc, bin->resolvedType);
|
||||||
|
delete bin->Right;
|
||||||
|
delete bin;
|
||||||
|
changed_ = true;
|
||||||
|
return lit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Binary dal: iki taraf da tam sayı sabiti ────────────────────
|
||||||
|
if (!isIntLit(bin->Left) || !isIntLit(bin->Right)) return bin;
|
||||||
|
if (!canFoldOp(bin->Operator)) return bin;
|
||||||
|
|
||||||
|
auto* llit = static_cast<LiteralNode*>(bin->Left);
|
||||||
|
auto* rlit = static_cast<LiteralNode*>(bin->Right);
|
||||||
|
int lv = getIntVal(llit);
|
||||||
|
int rv = getIntVal(rlit);
|
||||||
|
|
||||||
|
// W002: Derleme zamanı sıfıra bölme
|
||||||
|
if ((bin->Operator == TokenType::SLASH || bin->Operator == TokenType::PERCENT) && rv == 0) {
|
||||||
|
Diagnostic d;
|
||||||
|
d.level = DiagLevel::Warning;
|
||||||
|
d.code = "W002";
|
||||||
|
d.loc = bin->loc;
|
||||||
|
d.message = "Derleme zamanı sıfıra bölme — katlama yapılmadı";
|
||||||
|
diag_.report(d);
|
||||||
|
return bin; // fold etme
|
||||||
|
}
|
||||||
|
|
||||||
|
int result = computeOp(bin->Operator, lv, rv);
|
||||||
|
LiteralNode* lit = makeFoldedLit(result, bin->loc, bin->resolvedType);
|
||||||
|
|
||||||
|
delete bin->Left;
|
||||||
|
delete bin->Right;
|
||||||
|
delete bin;
|
||||||
|
changed_ = true;
|
||||||
|
return lit;
|
||||||
|
}
|
||||||
|
|
||||||
|
case ASTKind::Postfix: {
|
||||||
|
auto* pf = static_cast<PostfixNode*>(node);
|
||||||
|
ASTNode* newOp = fold(pf->operand);
|
||||||
|
if (newOp != pf->operand) { pf->operand = newOp; if (newOp) newOp->parent = pf; }
|
||||||
|
return pf;
|
||||||
|
}
|
||||||
|
|
||||||
|
case ASTKind::Call: {
|
||||||
|
auto* call = static_cast<CallExpressionNode*>(node);
|
||||||
|
if (call->callee) {
|
||||||
|
ASTNode* nc = fold(call->callee);
|
||||||
|
if (nc != call->callee) { call->callee = nc; if (nc) nc->parent = call; }
|
||||||
|
}
|
||||||
|
for (auto*& arg : call->arguments) {
|
||||||
|
ASTNode* na = fold(arg);
|
||||||
|
if (na != arg) { arg = na; if (na) na->parent = call; }
|
||||||
|
}
|
||||||
|
return call;
|
||||||
|
}
|
||||||
|
|
||||||
|
case ASTKind::MemberAccess: {
|
||||||
|
auto* ma = static_cast<MemberAccessNode*>(node);
|
||||||
|
ASTNode* no = fold(ma->object);
|
||||||
|
if (no != ma->object) { ma->object = no; if (no) no->parent = ma; }
|
||||||
|
return ma;
|
||||||
|
}
|
||||||
|
|
||||||
|
case ASTKind::IndexExpression: {
|
||||||
|
auto* ix = static_cast<IndexExpressionNode*>(node);
|
||||||
|
ASTNode* no = fold(ix->object);
|
||||||
|
if (no != ix->object) { ix->object = no; if (no) no->parent = ix; }
|
||||||
|
ASTNode* ni = fold(ix->index);
|
||||||
|
if (ni != ix->index) { ix->index = ni; if (ni) ni->parent = ix; }
|
||||||
|
return ix;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Literal ve Identifier değişmez
|
||||||
|
case ASTKind::Literal:
|
||||||
|
case ASTKind::Identifier:
|
||||||
|
return node;
|
||||||
|
|
||||||
|
// ── Deyimler (çocuklara iner) ────────────────────────────────────────
|
||||||
|
case ASTKind::ExpressionStatement: {
|
||||||
|
auto* es = static_cast<ExpressionStatementNode*>(node);
|
||||||
|
if (es->expression) {
|
||||||
|
ASTNode* ne = fold(es->expression);
|
||||||
|
if (ne != es->expression) { es->expression = ne; if (ne) ne->parent = es; }
|
||||||
|
}
|
||||||
|
return es;
|
||||||
|
}
|
||||||
|
|
||||||
|
case ASTKind::ReturnStatement: {
|
||||||
|
auto* rs = static_cast<ReturnStatementNode*>(node);
|
||||||
|
if (rs->value) {
|
||||||
|
ASTNode* nv = fold(rs->value);
|
||||||
|
if (nv != rs->value) { rs->value = nv; if (nv) nv->parent = rs; }
|
||||||
|
}
|
||||||
|
return rs;
|
||||||
|
}
|
||||||
|
|
||||||
|
case ASTKind::VariableDecl: {
|
||||||
|
auto* vd = static_cast<VariableDeclNode*>(node);
|
||||||
|
if (vd->initExpr) {
|
||||||
|
ASTNode* ni = fold(vd->initExpr);
|
||||||
|
if (ni != vd->initExpr) { vd->initExpr = ni; if (ni) ni->parent = vd; }
|
||||||
|
}
|
||||||
|
for (auto*& ch : vd->getChildren()) {
|
||||||
|
ASTNode* nc = fold(ch);
|
||||||
|
if (nc != ch) { ch = nc; if (nc) nc->parent = vd; }
|
||||||
|
}
|
||||||
|
return vd;
|
||||||
|
}
|
||||||
|
|
||||||
|
case ASTKind::IfStatement: {
|
||||||
|
auto* ifs = static_cast<IfStatementNode*>(node);
|
||||||
|
if (ifs->condition) {
|
||||||
|
ASTNode* nc = fold(ifs->condition);
|
||||||
|
if (nc != ifs->condition) { ifs->condition = nc; if (nc) nc->parent = ifs; }
|
||||||
|
}
|
||||||
|
if (ifs->thenBranch) fold(ifs->thenBranch);
|
||||||
|
if (ifs->elseBranch) fold(ifs->elseBranch);
|
||||||
|
return ifs;
|
||||||
|
}
|
||||||
|
|
||||||
|
case ASTKind::WhileStatement: {
|
||||||
|
auto* ws = static_cast<WhileStatementNode*>(node);
|
||||||
|
if (ws->condition) {
|
||||||
|
ASTNode* nc = fold(ws->condition);
|
||||||
|
if (nc != ws->condition) { ws->condition = nc; if (nc) nc->parent = ws; }
|
||||||
|
}
|
||||||
|
if (ws->body) fold(ws->body);
|
||||||
|
return ws;
|
||||||
|
}
|
||||||
|
|
||||||
|
case ASTKind::ForStatement: {
|
||||||
|
auto* fs = static_cast<ForStatementNode*>(node);
|
||||||
|
if (fs->init) { ASTNode* n = fold(fs->init); if (n != fs->init) { fs->init = n; if (n) n->parent = fs; } }
|
||||||
|
if (fs->condition) { ASTNode* n = fold(fs->condition); if (n != fs->condition) { fs->condition = n; if (n) n->parent = fs; } }
|
||||||
|
if (fs->update) { ASTNode* n = fold(fs->update); if (n != fs->update) { fs->update = n; if (n) n->parent = fs; } }
|
||||||
|
if (fs->body) fold(fs->body);
|
||||||
|
return fs;
|
||||||
|
}
|
||||||
|
|
||||||
|
case ASTKind::DoWhileStatement: {
|
||||||
|
auto* dw = static_cast<DoWhileStatementNode*>(node);
|
||||||
|
if (dw->body) fold(dw->body);
|
||||||
|
if (dw->condition) {
|
||||||
|
ASTNode* nc = fold(dw->condition);
|
||||||
|
if (nc != dw->condition) { dw->condition = nc; if (nc) nc->parent = dw; }
|
||||||
|
}
|
||||||
|
return dw;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Blok ve üst düzey ────────────────────────────────────────────────
|
||||||
|
case ASTKind::Block: {
|
||||||
|
auto* blk = static_cast<BlockNode*>(node);
|
||||||
|
for (auto*& ch : blk->getChildren()) {
|
||||||
|
ASTNode* nc = fold(ch);
|
||||||
|
if (nc != ch) { ch = nc; if (nc) nc->parent = blk; }
|
||||||
|
}
|
||||||
|
return blk;
|
||||||
|
}
|
||||||
|
|
||||||
|
case ASTKind::FunctionDecl: {
|
||||||
|
auto* fn = static_cast<FunctionDeclNode*>(node);
|
||||||
|
for (auto*& ch : fn->getChildren()) {
|
||||||
|
ASTNode* nc = fold(ch);
|
||||||
|
if (nc != ch) { ch = nc; if (nc) nc->parent = fn; }
|
||||||
|
}
|
||||||
|
return fn;
|
||||||
|
}
|
||||||
|
|
||||||
|
case ASTKind::Program: {
|
||||||
|
auto* prog = static_cast<ProgramNode*>(node);
|
||||||
|
for (auto*& ch : prog->getChildren()) {
|
||||||
|
ASTNode* nc = fold(ch);
|
||||||
|
if (nc != ch) { ch = nc; if (nc) nc->parent = prog; }
|
||||||
|
}
|
||||||
|
return prog;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
// Bilinmeyen veya işlenmeyen node — olduğu gibi bırak
|
||||||
|
for (auto*& ch : node->getChildren()) {
|
||||||
|
ASTNode* nc = fold(ch);
|
||||||
|
if (nc != ch) { ch = nc; if (nc) nc->parent = node; }
|
||||||
|
}
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // SAQUT_OPT_CONSTANT_FOLDING
|
||||||
|
|
@ -0,0 +1,110 @@
|
||||||
|
// ============================================================================
|
||||||
|
// saQut — Ölü Kod Eleme (Dead Code Elimination) Pass (ADR-009)
|
||||||
|
//
|
||||||
|
// Bir Block içinde ilk return/break/continue'dan sonra gelen deyimler
|
||||||
|
// erişilemez (unreachable) olarak işaretlenir ve silinir.
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
#ifndef SAQUT_OPT_DEAD_CODE_ELIM
|
||||||
|
#define SAQUT_OPT_DEAD_CODE_ELIM
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <algorithm>
|
||||||
|
#include "opt/optimization_pass.hpp"
|
||||||
|
#include "parser/nodes/statements.hpp"
|
||||||
|
#include "parser/nodes/declarations.hpp"
|
||||||
|
#include "parser/nodes/expressions.hpp"
|
||||||
|
#include "parser/nodes/program.hpp"
|
||||||
|
|
||||||
|
class DeadCodeElimPass : public OptimizationPass {
|
||||||
|
public:
|
||||||
|
bool run(ASTNode* root, SymbolTable*) override {
|
||||||
|
changed_ = false;
|
||||||
|
visit(root);
|
||||||
|
return changed_;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string& name() const override {
|
||||||
|
static std::string n = "dead-code-elim";
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool changed_ = false;
|
||||||
|
|
||||||
|
void visit(ASTNode* node) {
|
||||||
|
if (!node) return;
|
||||||
|
|
||||||
|
switch (node->kind) {
|
||||||
|
|
||||||
|
// ── Block: ana DCE mantığı ───────────────────────────────────────────
|
||||||
|
case ASTKind::Block: {
|
||||||
|
auto* blk = static_cast<BlockNode*>(node);
|
||||||
|
auto& ch = blk->getChildren();
|
||||||
|
bool term = false;
|
||||||
|
|
||||||
|
for (auto* child : ch) {
|
||||||
|
if (term) {
|
||||||
|
// Bu deyim erişilemez
|
||||||
|
if (auto* sn = dynamic_cast<StatementNode*>(child)) {
|
||||||
|
if (sn->isReachable) {
|
||||||
|
sn->isReachable = false;
|
||||||
|
changed_ = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (child->kind == ASTKind::ReturnStatement ||
|
||||||
|
child->kind == ASTKind::BreakStatement ||
|
||||||
|
child->kind == ASTKind::ContinueStatement)
|
||||||
|
term = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Erişilemez çocukları sil ve vektörden çıkar
|
||||||
|
ch.erase(std::remove_if(ch.begin(), ch.end(),
|
||||||
|
[](ASTNode* n) {
|
||||||
|
auto* sn = dynamic_cast<StatementNode*>(n);
|
||||||
|
return sn && !sn->isReachable;
|
||||||
|
}), ch.end());
|
||||||
|
|
||||||
|
// Alt bloklara da in
|
||||||
|
for (auto* child : ch) visit(child);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── İf/While/For gövdelerine in ─────────────────────────────────────
|
||||||
|
case ASTKind::IfStatement: {
|
||||||
|
auto* ifs = static_cast<IfStatementNode*>(node);
|
||||||
|
visit(ifs->thenBranch);
|
||||||
|
visit(ifs->elseBranch);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case ASTKind::WhileStatement: {
|
||||||
|
auto* ws = static_cast<WhileStatementNode*>(node);
|
||||||
|
visit(ws->body);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case ASTKind::ForStatement: {
|
||||||
|
auto* fs = static_cast<ForStatementNode*>(node);
|
||||||
|
visit(fs->body);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case ASTKind::DoWhileStatement: {
|
||||||
|
auto* dw = static_cast<DoWhileStatementNode*>(node);
|
||||||
|
visit(dw->body);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Üst düzey: fonksiyon gövdelerine in ─────────────────────────────
|
||||||
|
case ASTKind::FunctionDecl:
|
||||||
|
case ASTKind::Program:
|
||||||
|
default:
|
||||||
|
for (auto* ch : node->getChildren()) visit(ch);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // SAQUT_OPT_DEAD_CODE_ELIM
|
||||||
|
|
@ -0,0 +1,56 @@
|
||||||
|
// ============================================================================
|
||||||
|
// saQut — Optimizasyon Yöneticisi (ADR-007, ADR-009)
|
||||||
|
//
|
||||||
|
// 1. AST'yi klonlar (orijinal dokunulmaz).
|
||||||
|
// 2. Etkin pass'leri fixpoint döngüsüyle çalıştırır.
|
||||||
|
// 3. Optimize edilmiş klon sahipliğini döndürür (caller delete eder).
|
||||||
|
//
|
||||||
|
// Fixpoint garantisi: her pass yalnızca küçülten dönüşümler yapar
|
||||||
|
// (katlama: n düğüm → 1 düğüm; DCE: düğüm siler). Büyüten pass
|
||||||
|
// (inlining vb.) eklenirse maxFixpointRounds tavanı zorunludur (ADR-009).
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
#ifndef SAQUT_OPT_MANAGER
|
||||||
|
#define SAQUT_OPT_MANAGER
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <vector>
|
||||||
|
#include "core/config.hpp"
|
||||||
|
#include "opt/ast_clone.hpp"
|
||||||
|
#include "opt/optimization_pass.hpp"
|
||||||
|
#include "opt/constant_folding.hpp"
|
||||||
|
#include "opt/dead_code_elim.hpp"
|
||||||
|
#include "diagnostic/diagnostic_engine.hpp"
|
||||||
|
#include "symbol/symbol_table.hpp"
|
||||||
|
|
||||||
|
class OptimizationManager {
|
||||||
|
public:
|
||||||
|
explicit OptimizationManager(const CompilerConfig& cfg, DiagnosticEngine& diag) {
|
||||||
|
if (cfg.optConstantFolding)
|
||||||
|
passes_.push_back(std::make_unique<ConstantFoldingPass>(diag));
|
||||||
|
if (cfg.optDeadCodeElim)
|
||||||
|
passes_.push_back(std::make_unique<DeadCodeElimPass>());
|
||||||
|
maxRounds_ = cfg.maxFixpointRounds;
|
||||||
|
}
|
||||||
|
|
||||||
|
// optimize: AST'yi klonlar ve optimize edilmiş kopyayı döndürür.
|
||||||
|
// Dönen pointer caller'a aittir (delete edilmeli).
|
||||||
|
ASTNode* optimize(ASTNode* root, SymbolTable* table) {
|
||||||
|
ASTNode* clone = deepClone(root);
|
||||||
|
|
||||||
|
for (int round = 0; round < maxRounds_; ++round) {
|
||||||
|
bool anyChange = false;
|
||||||
|
for (auto& pass : passes_)
|
||||||
|
if (pass->run(clone, table)) anyChange = true;
|
||||||
|
if (!anyChange) break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return clone;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::vector<std::unique_ptr<OptimizationPass>> passes_;
|
||||||
|
int maxRounds_ = 10;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // SAQUT_OPT_MANAGER
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
#ifndef SAQUT_OPT_PASS
|
||||||
|
#define SAQUT_OPT_PASS
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include "parser/ast_node.hpp"
|
||||||
|
#include "symbol/symbol_table.hpp"
|
||||||
|
|
||||||
|
class OptimizationPass {
|
||||||
|
public:
|
||||||
|
virtual ~OptimizationPass() = default;
|
||||||
|
|
||||||
|
// Pass'i çalıştırır. En az bir dönüşüm yaptıysa true döndürür.
|
||||||
|
// root: optimize edilecek klonlanmış AST kökü (sahiplik değişmez).
|
||||||
|
virtual bool run(ASTNode* root, SymbolTable* table) = 0;
|
||||||
|
|
||||||
|
virtual const std::string& name() const = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // SAQUT_OPT_PASS
|
||||||
|
|
@ -1,513 +1,13 @@
|
||||||
// ============================================================================
|
|
||||||
// saQut Compiler — Soyut Sözdizim Ağacı (AST)
|
|
||||||
// ============================================================================
|
|
||||||
//
|
|
||||||
// DİZİN: src/parser/ast.hpp
|
|
||||||
// KATMAN: Katman 3 — Parser'ın ürettiği, IR'nin tükettiği
|
|
||||||
// BAĞIMLI: Token (src/parser/token.hpp), Tools (src/tools.hpp)
|
|
||||||
// KULLANAN: Parser (src/parser/parser.hpp), IR (src/ir/ir.hpp)
|
|
||||||
//
|
|
||||||
// AMAÇ:
|
|
||||||
// Kaynak kodun hiyerarşik, anlamsal gösterimi. Her dil yapısı (ifade,
|
|
||||||
// deyim, fonksiyon) bir AST düğümü ile temsil edilir.
|
|
||||||
//
|
|
||||||
// AST DÜĞÜM HİYERARŞİSİ:
|
|
||||||
// ASTNode (soyut taban)
|
|
||||||
// ├── ProgramNode : Kök düğüm, tüm üst seviye deklarasyonları tutar
|
|
||||||
// ├── FunctionDeclNode : Fonksiyon tanımı (int main() { ... })
|
|
||||||
// ├── BlockNode : { ... } bloğu, statement listesi
|
|
||||||
// ├── VariableDeclNode : Değişken tanımı (int x = 10;)
|
|
||||||
// ├── IfStatementNode : if/else
|
|
||||||
// ├── WhileStatementNode : while döngüsü
|
|
||||||
// ├── ForStatementNode : for döngüsü
|
|
||||||
// ├── DoWhileStatementNode : do-while döngüsü
|
|
||||||
// ├── ReturnStatementNode : return [ifade]
|
|
||||||
// ├── BreakStatementNode : break
|
|
||||||
// ├── ContinueStatementNode : continue
|
|
||||||
// ├── ExpressionStatementNode: ifade + ; (bir statement olarak)
|
|
||||||
// ├── BinaryExpressionNode : İkili işlem (a + b, a * b)
|
|
||||||
// ├── LiteralNode : Sabit değer (42, "hello", true)
|
|
||||||
// ├── IdentifierNode : Değişken/fonksiyon ismi
|
|
||||||
// └── PostfixNode : Son ek işlem (a++, a--)
|
|
||||||
//
|
|
||||||
// TASARIM KARARLARI:
|
|
||||||
// 1. ASTKind enum: Her düğüm tipi için bir enum değeri.
|
|
||||||
// RTTI (dynamic_cast) yerine manuel tip kontrolü sağlar.
|
|
||||||
// Daha hızlı ve hata ayıklaması kolay.
|
|
||||||
//
|
|
||||||
// 2. parent pointer: Her düğüm ebeveynini bilir.
|
|
||||||
// Yukarı doğru gezinme (ör: bir döngü içinde break'in hedefini bulma).
|
|
||||||
//
|
|
||||||
// 3. children vektörü (protected): Sadece addChild() ile ekleme.
|
|
||||||
// ProgramNode, FunctionDeclNode, BlockNode gibi liste tutan düğümler
|
|
||||||
// bu vektörü kullanır. İkili işlem gibi sabit sayıda çocuğu olan
|
|
||||||
// düğümler kendi üye değişkenlerini kullanır (Left, Right).
|
|
||||||
//
|
|
||||||
// 4. log() metodu: Her düğüm kendi alt ağacını girintili olarak yazdırır.
|
|
||||||
// Debug ve test için. Gerçek kod üretimi için kullanılmaz.
|
|
||||||
//
|
|
||||||
// BİLİNEN SINIRLAMALAR (TODO):
|
|
||||||
// TODO: Bellek yönetimi: AST düğümleri heap'te new ile oluşturuluyor,
|
|
||||||
// silme sorumluluğu yok (sızıntı). unique_ptr veya arena allocator.
|
|
||||||
// TODO: Ziyaretçi deseni (Visitor pattern) eklenerek log() ve IR
|
|
||||||
// üretimi ayrı sınıflara taşınabilir.
|
|
||||||
//
|
|
||||||
// ============================================================================
|
|
||||||
|
|
||||||
#ifndef SAQUT_AST
|
#ifndef SAQUT_AST
|
||||||
#define SAQUT_AST
|
#define SAQUT_AST
|
||||||
|
|
||||||
#include <iostream>
|
#include "parser/ast_node.hpp"
|
||||||
#include <vector>
|
#include "parser/nodes/program.hpp"
|
||||||
#include "parser/token.hpp"
|
#include "parser/nodes/declarations.hpp"
|
||||||
#include "tools.hpp"
|
#include "parser/nodes/statements.hpp"
|
||||||
|
#include "parser/nodes/binary_expr.hpp"
|
||||||
|
#include "parser/nodes/literal.hpp"
|
||||||
|
#include "parser/nodes/identifier.hpp"
|
||||||
|
#include "parser/nodes/expressions.hpp"
|
||||||
|
|
||||||
// ============================================================================
|
#endif
|
||||||
// ASTKind — AST Düğüm Tipi Enum'u
|
|
||||||
// ============================================================================
|
|
||||||
//
|
|
||||||
// Her AST düğüm sınıfı, constructor'ında kendi kind değerini atar.
|
|
||||||
// CodeGenerator (IR) ve diğer AST işlemcileri, düğümün tipini bu enum
|
|
||||||
// üzerinden belirler.
|
|
||||||
//
|
|
||||||
// İsimlendirme: Düğüm sınıf adları "Node" ile biter, enum değerleri bitmez.
|
|
||||||
// Örn: sınıf=IfStatementNode, enum=IfStatement
|
|
||||||
//
|
|
||||||
enum class ASTKind {
|
|
||||||
Program, // Kök düğüm
|
|
||||||
FunctionDecl, // Fonksiyon tanımı
|
|
||||||
Block, // { } bloğu
|
|
||||||
VariableDecl, // Değişken tanımı
|
|
||||||
BinaryExpression, // İkili işlem (a + b)
|
|
||||||
UnaryExpression, // Tekli işlem (-a, !a) — ileride kullanılacak
|
|
||||||
Literal, // Sabit değer
|
|
||||||
Identifier, // İsim referansı
|
|
||||||
Postfix, // Son ek (a++)
|
|
||||||
IfStatement, // if/else
|
|
||||||
ForStatement, // for
|
|
||||||
WhileStatement, // while
|
|
||||||
DoWhileStatement, // do-while
|
|
||||||
ReturnStatement, // return
|
|
||||||
BreakStatement, // break
|
|
||||||
ContinueStatement, // continue
|
|
||||||
ExpressionStatement, // ifade + ;
|
|
||||||
};
|
|
||||||
|
|
||||||
// ============================================================================
|
|
||||||
// ASTNode — Soyut Temel Sınıf
|
|
||||||
// ============================================================================
|
|
||||||
//
|
|
||||||
// Tüm AST düğümlerinin ortak atası. Minimum arayüz:
|
|
||||||
// - kind: Düğüm tipi (ASTKind enum)
|
|
||||||
// - parent: Ebeveyn düğüm (kök için nullptr)
|
|
||||||
// - addChild() / getChildren(): Çocuk yönetimi
|
|
||||||
// - log(): Debug çıktısı (virtual, her alt sınıf override eder)
|
|
||||||
//
|
|
||||||
class ASTNode {
|
|
||||||
public:
|
|
||||||
ASTKind kind; // Düğüm tipi (alt sınıf constructor'ında atanır)
|
|
||||||
ASTNode* parent = nullptr; // Ebeveyn düğüm (kök = nullptr)
|
|
||||||
|
|
||||||
virtual void log(int indent = 0) {
|
|
||||||
(void)indent; // Kullanılmayan parametre uyarısını sustur
|
|
||||||
std::cout << "<Unknown>\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
// Çocuk ekleme. Otomatik olarak parent pointer'ı ayarlar.
|
|
||||||
void addChild(ASTNode* child) {
|
|
||||||
children.push_back(child);
|
|
||||||
child->parent = this;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<ASTNode*>& getChildren() { return children; }
|
|
||||||
|
|
||||||
virtual ~ASTNode() = default;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
std::vector<ASTNode*> children; // Alt düğümler (liste tipi düğümler için)
|
|
||||||
};
|
|
||||||
|
|
||||||
// ============================================================================
|
|
||||||
// ProgramNode — Kök Düğüm
|
|
||||||
// ============================================================================
|
|
||||||
//
|
|
||||||
// Her saQut programı tek bir ProgramNode ile başlar.
|
|
||||||
// Çocukları: FunctionDeclNode, VariableDeclNode (global), ExpressionStatement.
|
|
||||||
//
|
|
||||||
class ProgramNode : public ASTNode {
|
|
||||||
public:
|
|
||||||
ProgramNode() { kind = ASTKind::Program; }
|
|
||||||
|
|
||||||
void log(int indent = 0) override {
|
|
||||||
std::cout << padRight("", indent) << "Program\n";
|
|
||||||
for (auto* c : getChildren())
|
|
||||||
c->log(indent + 2);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// ============================================================================
|
|
||||||
// FunctionDeclNode — Fonksiyon Tanımı
|
|
||||||
// ============================================================================
|
|
||||||
//
|
|
||||||
// Örnek: int main() { ... }
|
|
||||||
// returnType: "int", "void", "float", ...
|
|
||||||
// name: "main", "calculate", ...
|
|
||||||
// children: gövde (genellikle tek bir BlockNode)
|
|
||||||
//
|
|
||||||
// TODO: Parametre listesi (şu anda boş)
|
|
||||||
//
|
|
||||||
class FunctionDeclNode : public ASTNode {
|
|
||||||
public:
|
|
||||||
std::string name; // Fonksiyon adı
|
|
||||||
std::string returnType; // Dönüş tipi (string olarak, ileride tip sistemi)
|
|
||||||
|
|
||||||
FunctionDeclNode() { kind = ASTKind::FunctionDecl; }
|
|
||||||
|
|
||||||
void log(int indent = 0) override {
|
|
||||||
std::cout << padRight("", indent)
|
|
||||||
<< "FunctionDecl " << returnType << " " << name << "()\n";
|
|
||||||
for (auto* c : getChildren())
|
|
||||||
c->log(indent + 2);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// ============================================================================
|
|
||||||
// BlockNode — Blok { ... }
|
|
||||||
// ============================================================================
|
|
||||||
//
|
|
||||||
// Bir dizi statement'i gruplar. Kendi scope (kapsam) alanı oluşturur.
|
|
||||||
// Örnek: { int x = 1; x = x + 2; }
|
|
||||||
//
|
|
||||||
class BlockNode : public ASTNode {
|
|
||||||
public:
|
|
||||||
BlockNode() { kind = ASTKind::Block; }
|
|
||||||
|
|
||||||
void log(int indent = 0) override {
|
|
||||||
std::cout << padRight("", indent) << "Block\n";
|
|
||||||
for (auto* c : getChildren())
|
|
||||||
c->log(indent + 2);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// ============================================================================
|
|
||||||
// VariableDeclNode — Değişken Tanımı
|
|
||||||
// ============================================================================
|
|
||||||
//
|
|
||||||
// Örnek: int x = 10;
|
|
||||||
// varType: "int", "float", "bool", ...
|
|
||||||
// name: "x", "counter", ...
|
|
||||||
// initExpr: Başlangıç değeri (nullptr = tanımsız, örn: int x;)
|
|
||||||
//
|
|
||||||
class VariableDeclNode : public ASTNode {
|
|
||||||
public:
|
|
||||||
std::string varType; // Değişken tipi
|
|
||||||
std::string name; // Değişken adı
|
|
||||||
ASTNode* initExpr = nullptr; // Başlangıç ifadesi (opsiyonel)
|
|
||||||
|
|
||||||
VariableDeclNode() { kind = ASTKind::VariableDecl; }
|
|
||||||
|
|
||||||
void log(int indent = 0) override {
|
|
||||||
std::cout << padRight("", indent)
|
|
||||||
<< "VariableDecl " << varType << " " << name;
|
|
||||||
if (initExpr) {
|
|
||||||
std::cout << " =\n";
|
|
||||||
initExpr->log(indent + 4);
|
|
||||||
} else {
|
|
||||||
std::cout << "\n";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// ============================================================================
|
|
||||||
// BinaryExpressionNode — İkili İşlem (a OP b)
|
|
||||||
// ============================================================================
|
|
||||||
//
|
|
||||||
// İki operandlı tüm işlemler: a + b, a * b, a == b, a && b, ...
|
|
||||||
// Unary prefix operatörler de burada temsil edilir (Left = nullptr).
|
|
||||||
//
|
|
||||||
// Operator: İşlem tipi (PLUS, MINUS, STAR, EQUAL_EQUAL, ...)
|
|
||||||
// Left: Sol operand (unary prefix'te nullptr)
|
|
||||||
// Right: Sağ operand (her zaman dolu)
|
|
||||||
//
|
|
||||||
// NEDEN AYRI BİR UnaryExpressionNode YOK?
|
|
||||||
// Pratt parser'da unary ve binary operatörler aynı akışta işlenir.
|
|
||||||
// Left'in null olması unary olduğunu belirtir. Bu, kod tekrarını önler.
|
|
||||||
// İleride AST işlemcisi Left'e bakarak unary/binary ayrımı yapabilir.
|
|
||||||
//
|
|
||||||
class BinaryExpressionNode : public ASTNode {
|
|
||||||
public:
|
|
||||||
TokenType Operator; // İşlem tipi
|
|
||||||
ASTNode* Left = nullptr; // Sol operand
|
|
||||||
ASTNode* Right = nullptr; // Sağ operand
|
|
||||||
|
|
||||||
BinaryExpressionNode() { kind = ASTKind::BinaryExpression; }
|
|
||||||
|
|
||||||
void log(int indent = 0) override {
|
|
||||||
// Operatörün enum ismini ve sembolünü göster
|
|
||||||
auto it = OPERATOR_MAP_STRREV.find(Operator);
|
|
||||||
std::string sym = (it != OPERATOR_MAP_STRREV.end()) ? std::string(it->second) : "?";
|
|
||||||
std::string val;
|
|
||||||
auto it2 = OPERATOR_MAP_REV.find(Operator);
|
|
||||||
if (it2 != OPERATOR_MAP_REV.end()) val = std::string(it2->second);
|
|
||||||
|
|
||||||
std::cout << padRight("", indent) << "BinaryExpr " << sym
|
|
||||||
<< " (" << val << ")\n";
|
|
||||||
// Önce sağ, sonra sol yazdır — ağaç görselleştirmesi için
|
|
||||||
if (Right) Right->log(indent + 2);
|
|
||||||
if (Left) Left->log(indent + 2);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// ============================================================================
|
|
||||||
// LiteralNode — Sabit Değer
|
|
||||||
// ============================================================================
|
|
||||||
//
|
|
||||||
// Kaynak kodda doğrudan yazılan değerler: 42, "hello", true, false, null.
|
|
||||||
// lexerToken: Orijinal Token (NumberToken ise isFloat/base bilgisi)
|
|
||||||
// parserToken: Parser'ın atadığı tip bilgisi
|
|
||||||
//
|
|
||||||
class LiteralNode : public ASTNode {
|
|
||||||
public:
|
|
||||||
Token* lexerToken = nullptr; // Tokenizer'dan gelen orijinal token
|
|
||||||
ParserToken parserToken; // Parser tarafından zenginleştirilmiş token
|
|
||||||
|
|
||||||
LiteralNode() { kind = ASTKind::Literal; }
|
|
||||||
|
|
||||||
void log(int indent = 0) override {
|
|
||||||
std::cout << padRight("", indent)
|
|
||||||
<< "Literal {" << parserToken.token->token << "}\n";
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// ============================================================================
|
|
||||||
// IdentifierNode — Tanımlayıcı Referansı
|
|
||||||
// ============================================================================
|
|
||||||
//
|
|
||||||
// Değişken, fonksiyon, veya tip ismi. Örn: x, myVar, calculate.
|
|
||||||
// İleride symbol table ile çözümlenecek (bu değişken nerede tanımlı?).
|
|
||||||
//
|
|
||||||
class IdentifierNode : public ASTNode {
|
|
||||||
public:
|
|
||||||
Token* lexerToken = nullptr;
|
|
||||||
ParserToken parserToken;
|
|
||||||
|
|
||||||
IdentifierNode() { kind = ASTKind::Identifier; }
|
|
||||||
|
|
||||||
void log(int indent = 0) override {
|
|
||||||
std::cout << padRight("", indent)
|
|
||||||
<< "Identifier {" << parserToken.token->token << "}\n";
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// ============================================================================
|
|
||||||
// PostfixNode — Son Ek İşlem (a++, a--)
|
|
||||||
// ============================================================================
|
|
||||||
//
|
|
||||||
// Operand'dan SONRA gelen operatör. Şu anda sadece ++ ve --.
|
|
||||||
// operand: İşlem yapılan ifade (genellikle IdentifierNode)
|
|
||||||
// Operator: PLUS_PLUS veya MINUS_MINUS
|
|
||||||
//
|
|
||||||
class PostfixNode : public ASTNode {
|
|
||||||
public:
|
|
||||||
ASTNode* operand = nullptr; // İşlem yapılan ifade
|
|
||||||
TokenType Operator; // PLUS_PLUS veya MINUS_MINUS
|
|
||||||
|
|
||||||
PostfixNode() { kind = ASTKind::Postfix; }
|
|
||||||
|
|
||||||
void log(int indent = 0) override {
|
|
||||||
auto it = OPERATOR_MAP_STRREV.find(Operator);
|
|
||||||
std::string sym = (it != OPERATOR_MAP_STRREV.end()) ? std::string(it->second) : "?";
|
|
||||||
|
|
||||||
std::cout << padRight("", indent) << "Postfix " << sym;
|
|
||||||
auto it2 = OPERATOR_MAP_REV.find(Operator);
|
|
||||||
if (it2 != OPERATOR_MAP_REV.end())
|
|
||||||
std::cout << " (" << it2->second << ")";
|
|
||||||
std::cout << "\n";
|
|
||||||
if (operand) operand->log(indent + 2);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// ============================================================================
|
|
||||||
// IfStatementNode — if / else
|
|
||||||
// ============================================================================
|
|
||||||
//
|
|
||||||
// condition: Koşul ifadesi (parantez içindeki)
|
|
||||||
// thenBranch: if gövdesi (BlockNode veya tek statement)
|
|
||||||
// elseBranch: else gövdesi (opsiyonel, nullptr = else yok)
|
|
||||||
//
|
|
||||||
class IfStatementNode : public ASTNode {
|
|
||||||
public:
|
|
||||||
ASTNode* condition = nullptr; // Koşul
|
|
||||||
ASTNode* thenBranch = nullptr; // if gövdesi
|
|
||||||
ASTNode* elseBranch = nullptr; // else gövdesi (opsiyonel)
|
|
||||||
|
|
||||||
IfStatementNode() { kind = ASTKind::IfStatement; }
|
|
||||||
|
|
||||||
void log(int indent = 0) override {
|
|
||||||
std::cout << padRight("", indent) << "IfStatement\n";
|
|
||||||
std::cout << padRight("", indent + 2) << "Condition:\n";
|
|
||||||
if (condition) condition->log(indent + 4);
|
|
||||||
std::cout << padRight("", indent + 2) << "Then:\n";
|
|
||||||
if (thenBranch) thenBranch->log(indent + 4);
|
|
||||||
if (elseBranch) {
|
|
||||||
std::cout << padRight("", indent + 2) << "Else:\n";
|
|
||||||
elseBranch->log(indent + 4);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// ============================================================================
|
|
||||||
// WhileStatementNode — while Döngüsü
|
|
||||||
// ============================================================================
|
|
||||||
//
|
|
||||||
// while (condition) body
|
|
||||||
//
|
|
||||||
class WhileStatementNode : public ASTNode {
|
|
||||||
public:
|
|
||||||
ASTNode* condition = nullptr; // Döngü koşulu
|
|
||||||
ASTNode* body = nullptr; // Döngü gövdesi
|
|
||||||
|
|
||||||
WhileStatementNode() { kind = ASTKind::WhileStatement; }
|
|
||||||
|
|
||||||
void log(int indent = 0) override {
|
|
||||||
std::cout << padRight("", indent) << "WhileStatement\n";
|
|
||||||
std::cout << padRight("", indent + 2) << "Condition:\n";
|
|
||||||
if (condition) condition->log(indent + 4);
|
|
||||||
std::cout << padRight("", indent + 2) << "Body:\n";
|
|
||||||
if (body) body->log(indent + 4);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// ============================================================================
|
|
||||||
// ForStatementNode — for Döngüsü
|
|
||||||
// ============================================================================
|
|
||||||
//
|
|
||||||
// for (init; condition; update) body
|
|
||||||
//
|
|
||||||
// init: Başlangıç (VariableDeclNode veya ExpressionStatementNode)
|
|
||||||
// condition: Devam koşulu (nullptr = sonsuz döngü)
|
|
||||||
// update: Her adımda çalışan ifade
|
|
||||||
// body: Döngü gövdesi
|
|
||||||
//
|
|
||||||
class ForStatementNode : public ASTNode {
|
|
||||||
public:
|
|
||||||
ASTNode* init = nullptr; // Başlangıç
|
|
||||||
ASTNode* condition = nullptr; // Koşul
|
|
||||||
ASTNode* update = nullptr; // Güncelleme
|
|
||||||
ASTNode* body = nullptr; // Gövde
|
|
||||||
|
|
||||||
ForStatementNode() { kind = ASTKind::ForStatement; }
|
|
||||||
|
|
||||||
void log(int indent = 0) override {
|
|
||||||
std::cout << padRight("", indent) << "ForStatement\n";
|
|
||||||
if (init) {
|
|
||||||
std::cout << padRight("", indent + 2) << "Init:\n";
|
|
||||||
init->log(indent + 4);
|
|
||||||
}
|
|
||||||
if (condition) {
|
|
||||||
std::cout << padRight("", indent + 2) << "Condition:\n";
|
|
||||||
condition->log(indent + 4);
|
|
||||||
}
|
|
||||||
if (update) {
|
|
||||||
std::cout << padRight("", indent + 2) << "Update:\n";
|
|
||||||
update->log(indent + 4);
|
|
||||||
}
|
|
||||||
std::cout << padRight("", indent + 2) << "Body:\n";
|
|
||||||
if (body) body->log(indent + 4);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// ============================================================================
|
|
||||||
// DoWhileStatementNode — do-while Döngüsü
|
|
||||||
// ============================================================================
|
|
||||||
//
|
|
||||||
// do body while (condition);
|
|
||||||
//
|
|
||||||
class DoWhileStatementNode : public ASTNode {
|
|
||||||
public:
|
|
||||||
ASTNode* condition = nullptr;
|
|
||||||
ASTNode* body = nullptr;
|
|
||||||
|
|
||||||
DoWhileStatementNode() { kind = ASTKind::DoWhileStatement; }
|
|
||||||
|
|
||||||
void log(int indent = 0) override {
|
|
||||||
std::cout << padRight("", indent) << "DoWhileStatement\n";
|
|
||||||
std::cout << padRight("", indent + 2) << "Body:\n";
|
|
||||||
if (body) body->log(indent + 4);
|
|
||||||
std::cout << padRight("", indent + 2) << "Condition:\n";
|
|
||||||
if (condition) condition->log(indent + 4);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// ============================================================================
|
|
||||||
// ReturnStatementNode — return [ifade]
|
|
||||||
// ============================================================================
|
|
||||||
//
|
|
||||||
// value = nullptr ise "return;" (void fonksiyonda)
|
|
||||||
// value dolu ise "return expr;"
|
|
||||||
//
|
|
||||||
class ReturnStatementNode : public ASTNode {
|
|
||||||
public:
|
|
||||||
ASTNode* value = nullptr; // Dönüş değeri (opsiyonel)
|
|
||||||
|
|
||||||
ReturnStatementNode() { kind = ASTKind::ReturnStatement; }
|
|
||||||
|
|
||||||
void log(int indent = 0) override {
|
|
||||||
std::cout << padRight("", indent) << "ReturnStatement";
|
|
||||||
if (value) {
|
|
||||||
std::cout << "\n";
|
|
||||||
value->log(indent + 2);
|
|
||||||
} else {
|
|
||||||
std::cout << " (void)\n";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// ============================================================================
|
|
||||||
// BreakStatementNode — break
|
|
||||||
// ============================================================================
|
|
||||||
//
|
|
||||||
// En yakın döngüden veya switch'ten çıkar.
|
|
||||||
//
|
|
||||||
class BreakStatementNode : public ASTNode {
|
|
||||||
public:
|
|
||||||
BreakStatementNode() { kind = ASTKind::BreakStatement; }
|
|
||||||
void log(int indent = 0) override {
|
|
||||||
std::cout << padRight("", indent) << "BreakStatement\n";
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// ============================================================================
|
|
||||||
// ContinueStatementNode — continue
|
|
||||||
// ============================================================================
|
|
||||||
//
|
|
||||||
// En yakın döngünün başına atlar.
|
|
||||||
//
|
|
||||||
class ContinueStatementNode : public ASTNode {
|
|
||||||
public:
|
|
||||||
ContinueStatementNode() { kind = ASTKind::ContinueStatement; }
|
|
||||||
void log(int indent = 0) override {
|
|
||||||
std::cout << padRight("", indent) << "ContinueStatement\n";
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// ============================================================================
|
|
||||||
// ExpressionStatementNode — İfadeyi Statement Olarak Sarma
|
|
||||||
// ============================================================================
|
|
||||||
//
|
|
||||||
// Bir ifadeyi (expression) statement bağlamında kullanmak için sarar.
|
|
||||||
// Örn: x = 5; → ExpressionStatementNode( BinaryExpressionNode(x, =, 5) )
|
|
||||||
//
|
|
||||||
class ExpressionStatementNode : public ASTNode {
|
|
||||||
public:
|
|
||||||
ASTNode* expression = nullptr; // İç ifade
|
|
||||||
|
|
||||||
ExpressionStatementNode() { kind = ASTKind::ExpressionStatement; }
|
|
||||||
|
|
||||||
void log(int indent = 0) override {
|
|
||||||
std::cout << padRight("", indent) << "ExpressionStatement\n";
|
|
||||||
if (expression) expression->log(indent + 2);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // SAQUT_AST
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,244 @@
|
||||||
|
// ============================================================================
|
||||||
|
// saQut Compiler — Temiz JSON Üretici (JsonObject)
|
||||||
|
// ============================================================================
|
||||||
|
//
|
||||||
|
// DİZİN: src/parser/ast_json.hpp
|
||||||
|
// KATMAN: Katman 3 — Parser (AST JSON serileştirme)
|
||||||
|
// AMAÇ: AST düğümlerinin toJson() metotlarında kullanılan builder pattern
|
||||||
|
// BAĞIMLI: Yok (sadece <string>, <sstream>)
|
||||||
|
//
|
||||||
|
// AMAÇ:
|
||||||
|
// AST düğümlerinin toJson() metotlarını okunabilir kılmak.
|
||||||
|
// stringstream'i el ile yönetmek yerine builder pattern kullanır.
|
||||||
|
//
|
||||||
|
// KULLANIM:
|
||||||
|
// JsonObject obj(depth);
|
||||||
|
// obj.add("kind", "Literal");
|
||||||
|
// obj.add("value", 42);
|
||||||
|
// obj.add("location", loc.toJson()); // ham JSON gömme
|
||||||
|
// return obj.str();
|
||||||
|
//
|
||||||
|
// TASARIM KARARLARI:
|
||||||
|
// 1. Builder pattern: add() çağrıları zincirlenemez ama okunabilirlik kazanır.
|
||||||
|
// Zincirleme için: return obj.add("a",1).add("b",2).str() — tercih edilmedi.
|
||||||
|
// 2. addRaw(): Önceden formatlanmış JSON (alt düğüm çıktısı) gömmek için.
|
||||||
|
// 3. addArray(): Callback ile dizi oluşturma — C++ lambda'ları sayesinde temiz.
|
||||||
|
// 4. addIfNotEmpty/addIfNot: Koşullu alanlar — null alanları JSON'da göstermemek için.
|
||||||
|
// JSON çıktısını temiz tutar.
|
||||||
|
// 5. JSON_INDENT = 2: Standart JSON girinti (4 değil, 2 okunabilir).
|
||||||
|
//
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
#ifndef SAQUT_AST_JSON
|
||||||
|
#define SAQUT_AST_JSON
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// JSON_INDENT — JSON girinti miktarı (boşluk sayısı)
|
||||||
|
// ============================================================================
|
||||||
|
// tools.hpp'deki jsonIndent() ile uyumlu olmalıdır.
|
||||||
|
// Her seviyede 2 boşluk içe kaydırılır.
|
||||||
|
#define JSON_INDENT 2
|
||||||
|
|
||||||
|
// jsonEscape ve jsonIndent tools.hpp'de tanımlıdır.
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// JsonObject — JSON Nesne Builder
|
||||||
|
// ============================================================================
|
||||||
|
//
|
||||||
|
// AST düğümlerini JSON formatına dönüştürmek için kullanılır.
|
||||||
|
// Her çağrıda yeni bir JsonObject oluşturulur, alanlar eklenir ve str() ile
|
||||||
|
// JSON stringi alınır.
|
||||||
|
//
|
||||||
|
// KULLANIM:
|
||||||
|
// JsonObject obj(depth);
|
||||||
|
// obj.add("kind", "FunctionDecl");
|
||||||
|
// obj.add("name", name);
|
||||||
|
// obj.add("returnType", returnType);
|
||||||
|
// obj.addRaw("location", loc.toJson()); // önceden formatlanmış JSON
|
||||||
|
// obj.addArray("children", [&] { // alt düğümler
|
||||||
|
// for (auto* child : children)
|
||||||
|
// obj.addChild(child->toJson(depth + 2));
|
||||||
|
// });
|
||||||
|
// return obj.str();
|
||||||
|
//
|
||||||
|
// ÖRNEK ÇIKTI (depth=0):
|
||||||
|
// {
|
||||||
|
// "kind": "FunctionDecl",
|
||||||
|
// "name": "main",
|
||||||
|
// "returnType": "int",
|
||||||
|
// "children": [ ... ]
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
class JsonObject {
|
||||||
|
public:
|
||||||
|
// JsonObject — Yapıcı
|
||||||
|
// PARAMETRE: depth — JSON girinti seviyesi (0 = en dış)
|
||||||
|
// YAN ETKİ: m_ss'e açılış süslü parantezi yazar
|
||||||
|
// NOT: Açılış süslü parantezi kasıtlı olarak girintisiz yazılır.
|
||||||
|
// str() çıktısı her zaman bir "key": ya da dizi elemanı konumuna
|
||||||
|
// gömülür ve o konum zaten kendi girintisini sağlar; burada ayrıca
|
||||||
|
// m_indent eklenirse aynı boşluklar iki kez yazılır (bkz. addRaw/addItem).
|
||||||
|
JsonObject(int depth)
|
||||||
|
: m_indent(jsonIndent(depth)),
|
||||||
|
m_indentInner(jsonIndent(depth + 1))
|
||||||
|
{
|
||||||
|
m_ss << "{\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
// add() — String alan ekle
|
||||||
|
// PARAMETRELER:
|
||||||
|
// key — JSON anahtarı (tırnak içinde yazılır)
|
||||||
|
// value — string değer (otomatik tırnaklanır ve escape edilir)
|
||||||
|
// YAN ETKİ: m_hasFields true olur
|
||||||
|
// ÖRN: obj.add("name", "main") → "name": "main"
|
||||||
|
void add(const std::string& key, const std::string& value) {
|
||||||
|
addRaw(key, "\"" + jsonEscape(value) + "\"");
|
||||||
|
}
|
||||||
|
|
||||||
|
// add() — C string alanı ekle
|
||||||
|
// const char* literalleri (örn. "Block") bool overload'a kaymasın diye
|
||||||
|
// ayrı bir overload gerekir; aksi halde örtük const char* -> bool
|
||||||
|
// dönüşümü std::string'e öncelikli olur ve "kind": true gibi hatalı
|
||||||
|
// çıktı üretir.
|
||||||
|
void add(const std::string& key, const char* value) {
|
||||||
|
add(key, std::string(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
// add() — Sayısal alan ekle
|
||||||
|
// PARAMETRELER:
|
||||||
|
// key — JSON anahtarı
|
||||||
|
// value — tamsayı değer (tırnaklanmaz, olduğu gibi yazılır)
|
||||||
|
// ÖRN: obj.add("line", 42) → "line": 42
|
||||||
|
void add(const std::string& key, int value) {
|
||||||
|
addRaw(key, std::to_string(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
// add() — Boolean alan ekle
|
||||||
|
// PARAMETRELER:
|
||||||
|
// key — JSON anahtarı
|
||||||
|
// value — true/false
|
||||||
|
// ÖRN: obj.add("isPublic", true) → "isPublic": true
|
||||||
|
void add(const std::string& key, bool value) {
|
||||||
|
addRaw(key, value ? "true" : "false");
|
||||||
|
}
|
||||||
|
|
||||||
|
// addRaw() — Ham JSON değeri ekle (önceden formatlanmış)
|
||||||
|
// PARAMETRELER:
|
||||||
|
// key — JSON anahtarı
|
||||||
|
// jsonValue — önceden JSON'a çevrilmiş değer (tırnaklanmaz!)
|
||||||
|
// KULLANIM: Alt düğüm toJson() çıktısını gömmek için.
|
||||||
|
// addRaw("location", loc.toJson());
|
||||||
|
void addRaw(const std::string& key, const std::string& jsonValue) {
|
||||||
|
if (m_hasFields) m_ss << ",\n";
|
||||||
|
m_ss << m_indentInner << "\"" << jsonEscape(key) << "\": " << jsonValue;
|
||||||
|
m_hasFields = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// addNested() — Alt nesne ekle (addRaw alias)
|
||||||
|
// PARAMETRELER: addRaw ile aynı
|
||||||
|
// KULLANIM: addRaw ile aynı. Sadece okunabilirlik için.
|
||||||
|
void addNested(const std::string& key, const std::string& nestedJson) {
|
||||||
|
addRaw(key, nestedJson);
|
||||||
|
}
|
||||||
|
|
||||||
|
// addIfNotEmpty() — Koşullu string alan
|
||||||
|
// PARAMETRELER:
|
||||||
|
// key — JSON anahtarı
|
||||||
|
// value — string değer (sadece boş DEĞİLSE eklenir)
|
||||||
|
// KULLANIM: Opsiyonel alanlar için. JSON çıktısını temiz tutar.
|
||||||
|
// obj.addIfNotEmpty("defaultValue", defaultVal);
|
||||||
|
void addIfNotEmpty(const std::string& key, const std::string& value) {
|
||||||
|
if (!value.empty()) add(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// addIfNot() — Koşullu sayı alan
|
||||||
|
// PARAMETRELER:
|
||||||
|
// key — JSON anahtarı
|
||||||
|
// value — mevcut değer
|
||||||
|
// defaultValue — varsayılan değer
|
||||||
|
// EKLEME KOŞULU: value != defaultValue
|
||||||
|
// KULLANIM: Varsayılan değerler JSON'da tekrarlanmaz.
|
||||||
|
// obj.addIfNot("precedence", 0, 14);
|
||||||
|
void addIfNot(const std::string& key, int value, int defaultValue) {
|
||||||
|
if (value != defaultValue) add(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// addArray() — Dizi alanı (callback ile)
|
||||||
|
// PARAMETRELER:
|
||||||
|
// key — JSON anahtarı
|
||||||
|
// callback — dizi elemanlarını addItem ile ekleyen lambda/fonksiyon
|
||||||
|
// KULLANIM:
|
||||||
|
// obj.addArray("children", [&] {
|
||||||
|
// for (auto* child : children)
|
||||||
|
// obj.addItem(child->toJson(depth + 2));
|
||||||
|
// });
|
||||||
|
// ÖRNEK ÇIKTI:
|
||||||
|
// "children": [
|
||||||
|
// { "kind": "Literal", ... },
|
||||||
|
// { "kind": "Identifier", ... }
|
||||||
|
// ]
|
||||||
|
template<typename Fn>
|
||||||
|
void addArray(const std::string& key, Fn callback) {
|
||||||
|
if (m_hasFields) m_ss << ",\n";
|
||||||
|
m_ss << m_indentInner << "\"" << jsonEscape(key) << "\": [";
|
||||||
|
m_arrayDepth++;
|
||||||
|
bool saved = m_hasArrayItem;
|
||||||
|
m_hasArrayItem = false; // her dizi kendi virgül sayacıyla başlar
|
||||||
|
callback();
|
||||||
|
m_hasArrayItem = saved;
|
||||||
|
m_arrayDepth--;
|
||||||
|
m_ss << "\n" << m_indentInner << "]";
|
||||||
|
m_hasFields = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// addItem() — Diziye eleman ekle
|
||||||
|
// PARAMETRE: itemJson — JSON formatında dizi elemanı
|
||||||
|
// KULLANIM: Sadece addArray callback'i içinde kullanılır.
|
||||||
|
// YAN ETKİ: m_hasArrayItem true olur (virgül kontrolü için)
|
||||||
|
void addItem(const std::string& itemJson) {
|
||||||
|
if (m_hasArrayItem) m_ss << ",";
|
||||||
|
// Öğeler m_indentInner'in bir seviye altında (depth + 2)
|
||||||
|
std::string itemIndent = "";
|
||||||
|
itemIndent.append(m_indentInner.size() + 2, ' ');
|
||||||
|
m_ss << "\n" << itemIndent << itemJson;
|
||||||
|
m_hasArrayItem = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// str() — JSON nesnesini kapat ve string olarak döndür
|
||||||
|
// DÖNÜŞ: Tam JSON stringi ({"key": "value", ...})
|
||||||
|
// YAN ETKİ: Kapanış süslü parantezini ekler.
|
||||||
|
// KULLANIM:
|
||||||
|
// JsonObject obj(depth);
|
||||||
|
// obj.add("kind", "FunctionDecl");
|
||||||
|
// return obj.str();
|
||||||
|
std::string str() {
|
||||||
|
m_ss << "\n" << m_indent << "}";
|
||||||
|
return m_ss.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
/* ====== Builder State ====== */
|
||||||
|
std::ostringstream m_ss; // JSON çıktısının biriktirildiği string stream
|
||||||
|
|
||||||
|
std::string m_indent; // Bu nesnenin girinti seviyesi (depth * 2 boşluk)
|
||||||
|
// Örn: depth=0 → "", depth=1 → " "
|
||||||
|
|
||||||
|
std::string m_indentInner; // Bir alt seviye girinti ((depth+1) * 2 boşluk)
|
||||||
|
// Örn: depth=0 → " ", depth=1 → " "
|
||||||
|
|
||||||
|
bool m_hasFields = false; // Alan eklendi mi? (virgül kontrolü için)
|
||||||
|
// true ise bir sonraki alandan önce virgül + newline
|
||||||
|
|
||||||
|
int m_arrayDepth = 0; // İç içe dizi seviyesi (şu anda kullanılmıyor,
|
||||||
|
// ileride çok boyutlu diziler için)
|
||||||
|
|
||||||
|
bool m_hasArrayItem = false; // Diziye eleman eklendi mi? (virgül kontrolü)
|
||||||
|
// true ise bir sonraki elemandan önce virgül
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // SAQUT_AST_JSON
|
||||||
|
|
@ -0,0 +1,322 @@
|
||||||
|
// ============================================================================
|
||||||
|
// saQut Compiler — AST Düğüm Tabanı (ASTNode, ASTKind, LiteralType)
|
||||||
|
// ============================================================================
|
||||||
|
//
|
||||||
|
// DİZİN: src/parser/ast_node.hpp
|
||||||
|
// KATMAN: Katman 3 — Parser (Ayrıştırıcı)
|
||||||
|
// AMAÇ: Tüm AST düğümlerinin taban sınıfını ve temel enum'ları tanımlamak
|
||||||
|
//
|
||||||
|
// BAĞIMLILIKLAR:
|
||||||
|
// - core/location.hpp: Kaynak kod konum bilgisi (SourceLocation)
|
||||||
|
// - parser/token.hpp: Token tipleri (TokenType, ParserToken)
|
||||||
|
// - tools.hpp: Yardımcı fonksiyonlar (jsonIndent vb.)
|
||||||
|
//
|
||||||
|
// MİMARİ KARARLAR:
|
||||||
|
// 1. ASTNode, tüm düğümlerin ortak davranışını (log, toJson, children)
|
||||||
|
// tek bir yerde tanımlar. NVI (Non-Virtual Interface) pattern'i.
|
||||||
|
// 2. virtual log() ve toJson() — her düğüm kendi çıktısını kendisi üretir.
|
||||||
|
// 3. parent pointer — AST'de yukarı doğru gezinme (ör: sembol çözümleme).
|
||||||
|
// 4. children vector — aşağı doğru gezinme (ör: tüm düğümleri ziyaret).
|
||||||
|
// 5. ASTKind enum — switch/case ile tip kontrolü (dynamic_cast yerine).
|
||||||
|
// Performans: dynamic_cast < switch/case < virtual method çağrısı
|
||||||
|
// Ama switch/case ile yeni tip eklemek derleyici uyarısı verir (eksik case).
|
||||||
|
//
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
#ifndef SAQUT_AST_NODE
|
||||||
|
#define SAQUT_AST_NODE
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <sstream>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include "core/location.hpp"
|
||||||
|
#include "core/type.hpp"
|
||||||
|
#include "parser/token.hpp"
|
||||||
|
#include "tools.hpp"
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// ASTKind — Düğüm Tipi Enum
|
||||||
|
// ============================================================================
|
||||||
|
//
|
||||||
|
// Tüm AST düğüm tiplerini tanımlar. Her düğüm sınıfı bu enum'dan bir değer
|
||||||
|
// alır. enum class olması sayesinde isim çakışması olmaz (ASTKind::Program).
|
||||||
|
//
|
||||||
|
// NEDEN enum class, neden inheritance'daki typeid kullanılmıyor?
|
||||||
|
// - typeid().name() derleyiciye bağlıdır (g++: "4Program", MSVC: "class Program").
|
||||||
|
// - enum class her derleyicide aynıdır, string dönüşümü kolaydır.
|
||||||
|
// - static_cast<uint16_t> ile serileştirilebilir.
|
||||||
|
//
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
enum class ASTKind {
|
||||||
|
/* ====== En üst seviye ====== */
|
||||||
|
Program, // Kök düğüm — tüm programı kapsar.
|
||||||
|
// İçindeki children: FunctionDecl, StructDecl, VariableDecl.
|
||||||
|
// Tüm .cpp/.sqt dosyası tek bir Program düğümüdür.
|
||||||
|
|
||||||
|
/* ====== Tanımlar (Declarations) ====== */
|
||||||
|
FunctionDecl, // Fonksiyon tanımı.
|
||||||
|
// children: [returnType?], [name], [params...], [body: Block]
|
||||||
|
// Örn: int main() { ... }
|
||||||
|
StructDecl, // struct tanımı.
|
||||||
|
// children: [name], [members: VariableDecl...]
|
||||||
|
// Örn: struct Point { int x; int y; };
|
||||||
|
VariableDecl, // Değişken tanımı.
|
||||||
|
// children: [type?], [name], [initializer?]
|
||||||
|
// Örn: int x = 5; veya string name;
|
||||||
|
|
||||||
|
/* ====== Kontrol Akışı (Statements) ====== */
|
||||||
|
Block, // { } bloğu — birleşik ifade.
|
||||||
|
// children: [statements...]
|
||||||
|
// Kapsam (scope) oluşturur. Yerel değişkenler burada tanımlanır.
|
||||||
|
IfStatement, // if (koşul) gövde [else gövde].
|
||||||
|
// children: [condition], [thenBranch], [elseBranch?]
|
||||||
|
ForStatement, // for (init; koşul; artım) gövde.
|
||||||
|
// children: [init?], [condition?], [increment?], [body]
|
||||||
|
WhileStatement, // while (koşul) gövde.
|
||||||
|
// children: [condition], [body]
|
||||||
|
DoWhileStatement, // do gövde while (koşul);
|
||||||
|
// children: [body], [condition]
|
||||||
|
ReturnStatement, // return [ifade?];
|
||||||
|
// children: [value?]
|
||||||
|
BreakStatement, // break;
|
||||||
|
// children: yok. Sadece döngü/switch içinde geçerli.
|
||||||
|
ContinueStatement, // continue;
|
||||||
|
// children: yok. Sadece döngü içinde geçerli.
|
||||||
|
ExpressionStatement, // ifade + noktalı virgül (;)
|
||||||
|
// children: [expression]
|
||||||
|
// Örn: x = 5; veya foo();
|
||||||
|
|
||||||
|
/* ====== İfadeler (Expressions) ====== */
|
||||||
|
BinaryExpression, // İkili işlem: sol OP sağ.
|
||||||
|
// children: [left], [right]
|
||||||
|
// OP bilgisi: exprType alanında saklanır.
|
||||||
|
UnaryExpression, // Tekli işlem: OP operand.
|
||||||
|
// children: [operand]
|
||||||
|
// prefix (++x) veya postfix (x++) olabilir.
|
||||||
|
Literal, // Sabit değer: 42, 3.14, "hello", true, null.
|
||||||
|
// children: yok. Değer düğümün kendi alanında.
|
||||||
|
Identifier, // İsim referansı: x, PI, main.
|
||||||
|
// children: yok. İsim string olarak saklanır.
|
||||||
|
Postfix, // Postfix işlem: operand++.
|
||||||
|
// children: [operand]
|
||||||
|
Call, // Fonksiyon/metot çağrısı: f(args).
|
||||||
|
// children: [callee], [args...]
|
||||||
|
MemberAccess, // Üye erişimi: a.b veya a->b.
|
||||||
|
// children: [object], [member]
|
||||||
|
IndexExpression, // Dizi/indeks erişimi: a[i].
|
||||||
|
// children: [object], [index]
|
||||||
|
};
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// LiteralType — Sabit Değer Alt Tipleri
|
||||||
|
// ============================================================================
|
||||||
|
//
|
||||||
|
// Literal düğümünün hangi türde bir sabit değer taşıdığını belirler.
|
||||||
|
// uint8_t tabanlı — 256 farklı literal tipi yeterli.
|
||||||
|
//
|
||||||
|
// KULLANIM:
|
||||||
|
// Literal düğümü oluşturulurken tip belirtilir:
|
||||||
|
// Literal lit(LiteralType::INTEGER, "42");
|
||||||
|
//
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
enum class LiteralType : uint8_t {
|
||||||
|
INTEGER, // Tamsayı sabiti: 42, 0xFF, 0b1010, 0777
|
||||||
|
// Decimal, hexadecimal (0x), octal (0), binary (0b) desteklenir.
|
||||||
|
// Tokenizer NumberToken ile iletilir.
|
||||||
|
FLOAT, // Ondalıklı sayı: 3.14, 1e-5, 2.0f
|
||||||
|
// Nokta veya üs (e/E) içeren sayılar.
|
||||||
|
STRING, // Metin sabiti: "merhaba dünya"
|
||||||
|
// Çift tırnak içinde. Kaçış dizileri (\n, \t, \") desteklenir.
|
||||||
|
BOOLEAN, // Mantıksal değer: true / false
|
||||||
|
// KW_TRUE veya KW_FALSE token'ından gelir.
|
||||||
|
BOŞ // null sabiti (Türkçe "boş").
|
||||||
|
// KW_NULL token'ından gelir. Nesne/referans türleri için kullanılır.
|
||||||
|
};
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// literalTypeToString — LiteralType'ı string'e çevir (log için)
|
||||||
|
// ============================================================================
|
||||||
|
//
|
||||||
|
// PARAMETRE: t — LiteralType enum değeri
|
||||||
|
// DÖNÜŞ: const char* — insan tarafından okunabilir string
|
||||||
|
// KARMAŞIKLIK: O(1) — switch/case (derleyici jump table üretir)
|
||||||
|
//
|
||||||
|
inline const char* literalTypeToString(LiteralType t) {
|
||||||
|
switch (t) {
|
||||||
|
case LiteralType::INTEGER: return "integer";
|
||||||
|
case LiteralType::FLOAT: return "float";
|
||||||
|
case LiteralType::STRING: return "string";
|
||||||
|
case LiteralType::BOOLEAN: return "boolean";
|
||||||
|
case LiteralType::BOŞ: return "null";
|
||||||
|
}
|
||||||
|
return "?";
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// ASTNode — Soyut Taban Sınıf
|
||||||
|
// ============================================================================
|
||||||
|
//
|
||||||
|
// Tüm AST düğümleri bu sınıftan türetilir. Her düğüm:
|
||||||
|
// - kind: ASTKind enum — tipini bilir (switch/case için)
|
||||||
|
// - parent: Ebeveyn düğüme işaretçi (ağaçta yukarı gezinme)
|
||||||
|
// - loc: Kaynak koddaki satır/sütun konumu (hata mesajları için)
|
||||||
|
// - children: Alt düğümler (ağaçta aşağı gezinme)
|
||||||
|
// - log(): Konsola hiyerarşik yazdırma
|
||||||
|
// - toJson(): JSON formatında serileştirme
|
||||||
|
//
|
||||||
|
// KALITIM:
|
||||||
|
// Program : ASTNode — Kök düğüm
|
||||||
|
// FunctionDecl : ASTNode — Fonksiyon tanımı
|
||||||
|
// BinaryExpression : ASTNode — İkili işlem
|
||||||
|
// ... (her düğüm tipi ayrı sınıf)
|
||||||
|
//
|
||||||
|
// BELLEK YÖNETİMİ:
|
||||||
|
// Düğümler new ile oluşturulur, delete ile yok edilir.
|
||||||
|
// Sahiplik: Parser oluşturur, çağıran (main/CLI) yok eder.
|
||||||
|
// TODO(Büyük yeniden düzenleme): std::unique_ptr ile RAII.
|
||||||
|
//
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
class ASTNode {
|
||||||
|
public:
|
||||||
|
/* ====== Her düğümün tipi ====== */
|
||||||
|
ASTKind kind; // Düğüm tipi (Program, FunctionDecl, ...)
|
||||||
|
// switch(kind) ile tip kontrolü.
|
||||||
|
// Set edilir ve bir daha değişmez.
|
||||||
|
|
||||||
|
/* ====== Ağaç bağlantıları ====== */
|
||||||
|
ASTNode* parent = nullptr; // Ebeveyn düğüm pointerı.
|
||||||
|
// addChild() tarafından otomatik set edilir.
|
||||||
|
// Kullanım: semantic analizde kapsam bulma.
|
||||||
|
// Örn: değişkenin tanımlandığı fonksiyonu bulmak
|
||||||
|
// için parent->parent->... şeklinde yukarı çıkılır.
|
||||||
|
|
||||||
|
/* ====== Kaynak konumu ====== */
|
||||||
|
SourceLocation loc; // Tokenizer'dan gelen satır/sütun bilgisi.
|
||||||
|
// Hata mesajlarında: "satır 5, sütun 12"
|
||||||
|
// TODO: Şu anda tüm düğümlerde dolu değil.
|
||||||
|
|
||||||
|
/* ====== Sanal Metotlar ====== */
|
||||||
|
|
||||||
|
// log() — Düğümü ve alt düğümlerini konsola yazdırır.
|
||||||
|
// PARAMETRE: indent — girinti seviyesi (her seviyede 2 boşluk artar)
|
||||||
|
// KULLANIM: ast->log(0); // tüm ağacı yazdır
|
||||||
|
// KARMAŞIKLIK: O(n) — tüm alt ağacı dolaşır
|
||||||
|
virtual void log(int indent = 0) {
|
||||||
|
(void)indent;
|
||||||
|
std::cout << "<Unknown>\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
// toJson() — Düğümü ve alt düğümlerini JSON formatında döndürür.
|
||||||
|
// PARAMETRE: indent — JSON girinti seviyesi
|
||||||
|
// DÖNÜŞ: JSON stringi
|
||||||
|
// KULLANIM: std::string json = ast->toJson(0);
|
||||||
|
// KARMAŞIKLIK: O(n) — tüm alt ağacı dolaşır, string birleştirme maliyeti
|
||||||
|
virtual std::string toJson(int indent = 0) {
|
||||||
|
(void)indent;
|
||||||
|
return "{\"kind\":\"Unknown\"}";
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ====== Yardımcı Metotlar ====== */
|
||||||
|
|
||||||
|
// addChild() — Alt düğüm ekler ve parent pointer'ını set eder.
|
||||||
|
// PARAMETRE: child — eklenecek alt düğüm (nullptr olmamalı)
|
||||||
|
// YAN ETKİ: child->parent = this (otomatik)
|
||||||
|
// KARMAŞIKLIK: O(1) amortize — vector push_back
|
||||||
|
void addChild(ASTNode* child) {
|
||||||
|
children.push_back(child);
|
||||||
|
child->parent = this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// getChildren() — Alt düğüm vektörüne erişim.
|
||||||
|
// DÖNÜŞ: std::vector<ASTNode*>& — çocuk düğümler listesi
|
||||||
|
// KARMAŞIKLIK: O(1) — referans döndürür
|
||||||
|
std::vector<ASTNode*>& getChildren() { return children; }
|
||||||
|
|
||||||
|
// ~ASTNode() — Sanal yıkıcı (polimorfik silme için)
|
||||||
|
// delete ASTNode* yapıldığında doğru alt sınıf yıkıcısı çağrılır.
|
||||||
|
// Bu olmazsa türetilmiş sınıfların kaynakları sızdırılır.
|
||||||
|
virtual ~ASTNode() = default;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
// children — Alt düğümlerin vektörü.
|
||||||
|
// protected: doğrudan erişim yerine addChild/getChildren kullanılır.
|
||||||
|
// Türetilmiş sınıflar erişebilir (ör: log() içinde çocukları gezme).
|
||||||
|
std::vector<ASTNode*> children;
|
||||||
|
};
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// ExpressionNode — Değer Üreten Düğümlerin Tabanı (Faz 1, ADR-012)
|
||||||
|
// ============================================================================
|
||||||
|
//
|
||||||
|
// Bir DEĞER üreten her düğüm (Literal, Identifier, BinaryExpression, Call,
|
||||||
|
// Postfix, MemberAccess, IndexExpression) buradan türer. Bir ifadenin bir
|
||||||
|
// TİPİ vardır; analiz/optimizasyon alanları burada toplanır.
|
||||||
|
//
|
||||||
|
class ExpressionNode : public ASTNode {
|
||||||
|
public:
|
||||||
|
// TODO(faz-3): tip denetleyici doldurur. Şimdilik Error = "henüz çözülmedi".
|
||||||
|
Type resolvedType;
|
||||||
|
|
||||||
|
// TODO(faz-4): sabit katlama (constant folding) bayrağı.
|
||||||
|
bool isConstant = false;
|
||||||
|
// TODO(faz-4): foldedValue — katlanmış sabit değer (temsil Faz 4'te netleşir).
|
||||||
|
|
||||||
|
// resolvedType'ın JSON karşılığı (henüz çözülmemişse null gösterilir).
|
||||||
|
std::string resolvedTypeJson() const {
|
||||||
|
return resolvedType.isError() ? std::string("null") : resolvedType.toJson();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// StatementNode — Eylem/Kontrol Akışı Yürüten Düğümlerin Tabanı (Faz 1)
|
||||||
|
// ============================================================================
|
||||||
|
//
|
||||||
|
// Değer üretmeyen, bir iş/kontrol akışı yürüten her düğüm (Block, If, For,
|
||||||
|
// While, DoWhile, Return, Break, Continue, ExpressionStatement ve şimdilik
|
||||||
|
// VariableDecl) buradan türer. Tipi yoktur; akış-analizi alanları taşır.
|
||||||
|
//
|
||||||
|
// TODO(faz-1 gözden geçirme): VariableDecl/FunctionDecl/StructDecl'in tam
|
||||||
|
// sınıflandırması provizyonel — VariableDecl burada (blok içinde erişilebilirliğe
|
||||||
|
// tabi), Function/StructDecl doğrudan ASTNode altında kaldı.
|
||||||
|
//
|
||||||
|
class StatementNode : public ASTNode {
|
||||||
|
public:
|
||||||
|
// TODO(faz-3/4): erişilebilirlik (dead-code) analizi günceller.
|
||||||
|
bool isReachable = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// childrenToJson — Düğümün çocuklarını JSON array olarak yaz
|
||||||
|
// ============================================================================
|
||||||
|
//
|
||||||
|
// Bir düğümün tüm alt düğümlerini dolaşır ve her birinin toJson() çıktısını
|
||||||
|
// virgülle ayrılmış şekilde birleştirir.
|
||||||
|
//
|
||||||
|
// PARAMETRELER:
|
||||||
|
// node — çocukları yazdırılacak düğüm
|
||||||
|
// depth — JSON girinti seviyesi
|
||||||
|
//
|
||||||
|
// DÖNÜŞ: JSON array içeriği (köşeli parantezler HARİÇ)
|
||||||
|
//
|
||||||
|
// KULLANIM:
|
||||||
|
// std::string json = childrenToJson(this, depth + 1);
|
||||||
|
//
|
||||||
|
// KARMAŞIKLIK: O(n) — n = çocuk sayısı
|
||||||
|
//
|
||||||
|
inline std::string childrenToJson(ASTNode* node, int depth) {
|
||||||
|
std::ostringstream ss;
|
||||||
|
std::string in = jsonIndent(depth);
|
||||||
|
auto& ch = node->getChildren();
|
||||||
|
for (size_t i = 0; i < ch.size(); i++) {
|
||||||
|
ss << ch[i]->toJson(depth);
|
||||||
|
if (i + 1 < ch.size()) ss << ","; // son elemandan sonra virgül yok
|
||||||
|
ss << "\n";
|
||||||
|
}
|
||||||
|
return ss.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // SAQUT_AST_NODE
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
#include "parser/nodes/binary_expr.hpp"
|
||||||
|
#include "parser/ast_json.hpp"
|
||||||
|
|
||||||
|
BinaryExpressionNode::BinaryExpressionNode() {
|
||||||
|
kind = ASTKind::BinaryExpression;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BinaryExpressionNode::log(int indent) {
|
||||||
|
std::string in = jsonIndent(indent);
|
||||||
|
std::cout << in << "BinaryExpression (" << (OPERATOR_MAP_REV.count(Operator) ? OPERATOR_MAP_REV.at(Operator) : "?") << ")\n";
|
||||||
|
if (Left) Left->log(indent + 1);
|
||||||
|
if (Right) Right->log(indent + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string BinaryExpressionNode::toJson(int depth) {
|
||||||
|
JsonObject obj(depth);
|
||||||
|
obj.add("kind", "BinaryExpression");
|
||||||
|
obj.add("operator", std::string(OPERATOR_MAP_REV.count(Operator) ? OPERATOR_MAP_REV.at(Operator) : "?"));
|
||||||
|
if (Left) obj.addRaw("left", Left->toJson(depth + 1));
|
||||||
|
if (Right) obj.addRaw("right", Right->toJson(depth + 1));
|
||||||
|
obj.addRaw("resolvedType", resolvedTypeJson());
|
||||||
|
obj.addRaw("location", loc.toJson());
|
||||||
|
return obj.str();
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
#ifndef SAQUT_AST_BINARY_EXPR
|
||||||
|
#define SAQUT_AST_BINARY_EXPR
|
||||||
|
|
||||||
|
#include "parser/ast_node.hpp"
|
||||||
|
|
||||||
|
class BinaryExpressionNode : public ExpressionNode {
|
||||||
|
public:
|
||||||
|
TokenType Operator;
|
||||||
|
ASTNode* Left = nullptr;
|
||||||
|
ASTNode* Right = nullptr;
|
||||||
|
|
||||||
|
BinaryExpressionNode();
|
||||||
|
void log(int indent = 0) override;
|
||||||
|
std::string toJson(int depth = 0) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -0,0 +1,57 @@
|
||||||
|
#include "parser/nodes/declarations.hpp"
|
||||||
|
#include "parser/ast_json.hpp"
|
||||||
|
|
||||||
|
// FunctionDeclNode
|
||||||
|
FunctionDeclNode::FunctionDeclNode() { kind = ASTKind::FunctionDecl; }
|
||||||
|
void FunctionDeclNode::log(int indent) {
|
||||||
|
std::cout << jsonIndent(indent) << "FunctionDecl (" << name << " : " << returnType << ")\n";
|
||||||
|
for (auto* child : children) child->log(indent + 1);
|
||||||
|
}
|
||||||
|
std::string FunctionDeclNode::toJson(int depth) {
|
||||||
|
JsonObject obj(depth);
|
||||||
|
obj.add("kind", "FunctionDecl");
|
||||||
|
obj.add("name", name);
|
||||||
|
obj.add("returnType", returnType);
|
||||||
|
obj.addArray("params", [&]() {
|
||||||
|
for (auto* p : params) obj.addItem(p->toJson(depth + 2));
|
||||||
|
});
|
||||||
|
obj.addArray("children", [&]() {
|
||||||
|
for (auto* child : children) obj.addItem(child->toJson(depth + 2));
|
||||||
|
});
|
||||||
|
obj.addRaw("location", loc.toJson());
|
||||||
|
return obj.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
// VariableDeclNode
|
||||||
|
VariableDeclNode::VariableDeclNode() { kind = ASTKind::VariableDecl; }
|
||||||
|
void VariableDeclNode::log(int indent) {
|
||||||
|
std::cout << jsonIndent(indent) << "VariableDecl (" << name << " : " << varType << ")\n";
|
||||||
|
if (initExpr) initExpr->log(indent + 1);
|
||||||
|
}
|
||||||
|
std::string VariableDeclNode::toJson(int depth) {
|
||||||
|
JsonObject obj(depth);
|
||||||
|
obj.add("kind", "VariableDecl");
|
||||||
|
obj.add("name", name);
|
||||||
|
obj.add("varType", varType);
|
||||||
|
obj.add("isReachable", isReachable);
|
||||||
|
if (initExpr) obj.addRaw("init", initExpr->toJson(depth + 1));
|
||||||
|
obj.addRaw("location", loc.toJson());
|
||||||
|
return obj.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
// StructDeclNode
|
||||||
|
StructDeclNode::StructDeclNode() { kind = ASTKind::StructDecl; }
|
||||||
|
void StructDeclNode::log(int indent) {
|
||||||
|
std::cout << jsonIndent(indent) << "StructDecl (" << name << ")\n";
|
||||||
|
for (auto* child : children) child->log(indent + 1);
|
||||||
|
}
|
||||||
|
std::string StructDeclNode::toJson(int depth) {
|
||||||
|
JsonObject obj(depth);
|
||||||
|
obj.add("kind", "StructDecl");
|
||||||
|
obj.add("name", name);
|
||||||
|
obj.addArray("children", [&]() {
|
||||||
|
for (auto* child : children) obj.addItem(child->toJson(depth + 2));
|
||||||
|
});
|
||||||
|
obj.addRaw("location", loc.toJson());
|
||||||
|
return obj.str();
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,36 @@
|
||||||
|
#ifndef SAQUT_AST_DECL
|
||||||
|
#define SAQUT_AST_DECL
|
||||||
|
|
||||||
|
#include "parser/ast_node.hpp"
|
||||||
|
|
||||||
|
class VariableDeclNode; // fwd — FunctionDeclNode::params için
|
||||||
|
|
||||||
|
class FunctionDeclNode : public ASTNode {
|
||||||
|
public:
|
||||||
|
std::string name;
|
||||||
|
std::string returnType;
|
||||||
|
std::vector<VariableDeclNode*> params; // TODO(faz2): parametreler
|
||||||
|
FunctionDeclNode();
|
||||||
|
void log(int indent = 0) override;
|
||||||
|
std::string toJson(int depth = 0) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class VariableDeclNode : public StatementNode {
|
||||||
|
public:
|
||||||
|
std::string varType;
|
||||||
|
std::string name;
|
||||||
|
ASTNode* initExpr = nullptr;
|
||||||
|
VariableDeclNode();
|
||||||
|
void log(int indent = 0) override;
|
||||||
|
std::string toJson(int depth = 0) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class StructDeclNode : public ASTNode {
|
||||||
|
public:
|
||||||
|
std::string name;
|
||||||
|
StructDeclNode();
|
||||||
|
void log(int indent = 0) override;
|
||||||
|
std::string toJson(int depth = 0) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -0,0 +1,71 @@
|
||||||
|
#include "parser/nodes/expressions.hpp"
|
||||||
|
#include "parser/ast_json.hpp"
|
||||||
|
|
||||||
|
// PostfixNode
|
||||||
|
PostfixNode::PostfixNode() { kind = ASTKind::Postfix; }
|
||||||
|
void PostfixNode::log(int indent) {
|
||||||
|
std::cout << jsonIndent(indent) << "Postfix (" << (OPERATOR_MAP_REV.count(Operator) ? OPERATOR_MAP_REV.at(Operator) : "?") << ")\n";
|
||||||
|
if (operand) operand->log(indent + 1);
|
||||||
|
}
|
||||||
|
std::string PostfixNode::toJson(int depth) {
|
||||||
|
JsonObject obj(depth);
|
||||||
|
obj.add("kind", "Postfix");
|
||||||
|
obj.add("operator", std::string(OPERATOR_MAP_REV.count(Operator) ? OPERATOR_MAP_REV.at(Operator) : "?"));
|
||||||
|
if (operand) obj.addRaw("operand", operand->toJson(depth + 1));
|
||||||
|
obj.addRaw("resolvedType", resolvedTypeJson());
|
||||||
|
obj.addRaw("location", loc.toJson());
|
||||||
|
return obj.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
// CallExpressionNode
|
||||||
|
CallExpressionNode::CallExpressionNode() { kind = ASTKind::Call; }
|
||||||
|
void CallExpressionNode::log(int indent) {
|
||||||
|
std::cout << jsonIndent(indent) << "Call\n";
|
||||||
|
if (callee) callee->log(indent + 1);
|
||||||
|
for (auto* arg : arguments) arg->log(indent + 1);
|
||||||
|
}
|
||||||
|
std::string CallExpressionNode::toJson(int depth) {
|
||||||
|
JsonObject obj(depth);
|
||||||
|
obj.add("kind", "Call");
|
||||||
|
if (callee) obj.addRaw("callee", callee->toJson(depth + 1));
|
||||||
|
obj.addArray("arguments", [&]() {
|
||||||
|
for (auto* arg : arguments) obj.addItem(arg->toJson(depth + 2));
|
||||||
|
});
|
||||||
|
obj.addRaw("resolvedType", resolvedTypeJson());
|
||||||
|
obj.addRaw("location", loc.toJson());
|
||||||
|
return obj.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
// MemberAccessNode
|
||||||
|
MemberAccessNode::MemberAccessNode() { kind = ASTKind::MemberAccess; }
|
||||||
|
void MemberAccessNode::log(int indent) {
|
||||||
|
std::cout << jsonIndent(indent) << "MemberAccess (" << (arrow ? "->" : ".") << member << ")\n";
|
||||||
|
if (object) object->log(indent + 1);
|
||||||
|
}
|
||||||
|
std::string MemberAccessNode::toJson(int depth) {
|
||||||
|
JsonObject obj(depth);
|
||||||
|
obj.add("kind", "MemberAccess");
|
||||||
|
obj.add("member", member);
|
||||||
|
obj.add("arrow", arrow);
|
||||||
|
if (object) obj.addRaw("object", object->toJson(depth + 1));
|
||||||
|
obj.addRaw("resolvedType", resolvedTypeJson());
|
||||||
|
obj.addRaw("location", loc.toJson());
|
||||||
|
return obj.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
// IndexExpressionNode
|
||||||
|
IndexExpressionNode::IndexExpressionNode() { kind = ASTKind::IndexExpression; }
|
||||||
|
void IndexExpressionNode::log(int indent) {
|
||||||
|
std::cout << jsonIndent(indent) << "IndexExpression\n";
|
||||||
|
if (object) object->log(indent + 1);
|
||||||
|
if (index) index->log(indent + 1);
|
||||||
|
}
|
||||||
|
std::string IndexExpressionNode::toJson(int depth) {
|
||||||
|
JsonObject obj(depth);
|
||||||
|
obj.add("kind", "IndexExpression");
|
||||||
|
if (object) obj.addRaw("object", object->toJson(depth + 1));
|
||||||
|
if (index) obj.addRaw("index", index->toJson(depth + 1));
|
||||||
|
obj.addRaw("resolvedType", resolvedTypeJson());
|
||||||
|
obj.addRaw("location", loc.toJson());
|
||||||
|
return obj.str();
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,43 @@
|
||||||
|
#ifndef SAQUT_AST_EXPR_EXT
|
||||||
|
#define SAQUT_AST_EXPR_EXT
|
||||||
|
|
||||||
|
#include "parser/ast_node.hpp"
|
||||||
|
|
||||||
|
class PostfixNode : public ExpressionNode {
|
||||||
|
public:
|
||||||
|
ASTNode* operand = nullptr;
|
||||||
|
TokenType Operator;
|
||||||
|
PostfixNode();
|
||||||
|
void log(int indent = 0) override;
|
||||||
|
std::string toJson(int depth = 0) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class CallExpressionNode : public ExpressionNode {
|
||||||
|
public:
|
||||||
|
ASTNode* callee = nullptr;
|
||||||
|
std::vector<ASTNode*> arguments;
|
||||||
|
CallExpressionNode();
|
||||||
|
void log(int indent = 0) override;
|
||||||
|
std::string toJson(int depth = 0) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class MemberAccessNode : public ExpressionNode {
|
||||||
|
public:
|
||||||
|
ASTNode* object = nullptr;
|
||||||
|
std::string member;
|
||||||
|
bool arrow = false;
|
||||||
|
MemberAccessNode();
|
||||||
|
void log(int indent = 0) override;
|
||||||
|
std::string toJson(int depth = 0) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class IndexExpressionNode : public ExpressionNode {
|
||||||
|
public:
|
||||||
|
ASTNode* object = nullptr;
|
||||||
|
ASTNode* index = nullptr;
|
||||||
|
IndexExpressionNode();
|
||||||
|
void log(int indent = 0) override;
|
||||||
|
std::string toJson(int depth = 0) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
#include "parser/nodes/identifier.hpp"
|
||||||
|
#include <iostream>
|
||||||
|
#include <sstream>
|
||||||
|
#include "parser/ast_json.hpp"
|
||||||
|
|
||||||
|
IdentifierNode::IdentifierNode() { kind = ASTKind::Identifier; }
|
||||||
|
|
||||||
|
void IdentifierNode::log(int indent) {
|
||||||
|
std::cout << padRight("", indent)
|
||||||
|
<< "Identifier {" << (parserToken.token ? parserToken.token->token : "?") << "}\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string IdentifierNode::toJson(int depth) {
|
||||||
|
std::string in = jsonIndent(depth);
|
||||||
|
std::string name = parserToken.token ? parserToken.token->token : "?";
|
||||||
|
std::ostringstream ss;
|
||||||
|
ss << "{\n"
|
||||||
|
<< in << " \"kind\": \"Identifier\",\n"
|
||||||
|
<< in << " \"name\": \"" << jsonEscape(name) << "\",\n"
|
||||||
|
<< in << " \"resolvedType\": " << resolvedTypeJson() << ",\n"
|
||||||
|
<< in << " \"location\": " << loc.toJson() << "\n"
|
||||||
|
<< in << "}";
|
||||||
|
return ss.str();
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
#ifndef SAQUT_AST_IDENTIFIER
|
||||||
|
#define SAQUT_AST_IDENTIFIER
|
||||||
|
|
||||||
|
#include "parser/ast_node.hpp"
|
||||||
|
|
||||||
|
struct Symbol; // TODO(faz-2): sembol tablosu (Symbol) tanımlandığında bağlanacak
|
||||||
|
|
||||||
|
class IdentifierNode : public ExpressionNode {
|
||||||
|
public:
|
||||||
|
Token* lexerToken = nullptr;
|
||||||
|
ParserToken parserToken;
|
||||||
|
|
||||||
|
// TODO(faz-2): isim çözümlemede sembol tablosundaki tanıma bağlanır.
|
||||||
|
Symbol* resolvedSymbol = nullptr;
|
||||||
|
|
||||||
|
IdentifierNode();
|
||||||
|
void log(int indent = 0) override;
|
||||||
|
std::string toJson(int depth = 0) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -0,0 +1,39 @@
|
||||||
|
#include "parser/nodes/literal.hpp"
|
||||||
|
#include <iostream>
|
||||||
|
#include <sstream>
|
||||||
|
#include "parser/ast_json.hpp"
|
||||||
|
|
||||||
|
LiteralNode::LiteralNode() { kind = ASTKind::Literal; }
|
||||||
|
|
||||||
|
void LiteralNode::log(int indent) {
|
||||||
|
std::string val = hasDirectValue ? std::to_string(directIntValue)
|
||||||
|
: (parserToken.token ? parserToken.token->token : "?");
|
||||||
|
std::cout << padRight("", indent)
|
||||||
|
<< "Literal {" << val << "} "
|
||||||
|
<< literalTypeToString(literalType);
|
||||||
|
if (isConstant) std::cout << " [folded]";
|
||||||
|
if (literalType == LiteralType::INTEGER && literalBase != 10)
|
||||||
|
std::cout << " (base " << literalBase << ")";
|
||||||
|
std::cout << "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string LiteralNode::toJson(int depth) {
|
||||||
|
std::string in = jsonIndent(depth);
|
||||||
|
std::string val = hasDirectValue ? std::to_string(directIntValue)
|
||||||
|
: (parserToken.token ? parserToken.token->token : "?");
|
||||||
|
std::ostringstream ss;
|
||||||
|
ss << "{\n"
|
||||||
|
<< in << " \"kind\": \"Literal\",\n"
|
||||||
|
<< in << " \"literalType\": \"" << literalTypeToString(literalType) << "\",\n"
|
||||||
|
<< in << " \"value\": \"" << jsonEscape(val) << "\"";
|
||||||
|
if (literalType == LiteralType::INTEGER && literalBase != 10) {
|
||||||
|
ss << ",\n" << in << " \"base\": " << literalBase;
|
||||||
|
}
|
||||||
|
if (literalType == LiteralType::FLOAT) {
|
||||||
|
ss << ",\n" << in << " \"isFloat\": true";
|
||||||
|
}
|
||||||
|
ss << ",\n" << in << " \"resolvedType\": " << resolvedTypeJson();
|
||||||
|
ss << ",\n" << in << " \"location\": " << loc.toJson() << "\n"
|
||||||
|
<< in << "}";
|
||||||
|
return ss.str();
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
#ifndef SAQUT_AST_LITERAL
|
||||||
|
#define SAQUT_AST_LITERAL
|
||||||
|
|
||||||
|
#include "parser/ast_node.hpp"
|
||||||
|
|
||||||
|
class LiteralNode : public ExpressionNode {
|
||||||
|
public:
|
||||||
|
Token* lexerToken = nullptr;
|
||||||
|
ParserToken parserToken;
|
||||||
|
|
||||||
|
LiteralType literalType = LiteralType::INTEGER;
|
||||||
|
int literalBase = 10;
|
||||||
|
bool isFloatValue = false;
|
||||||
|
|
||||||
|
// Sabit katlama (constant folding) tarafından üretilen sentetik literal.
|
||||||
|
// parserToken.token yerine bu değer kullanılır.
|
||||||
|
bool hasDirectValue = false;
|
||||||
|
int directIntValue = 0;
|
||||||
|
|
||||||
|
LiteralNode();
|
||||||
|
void log(int indent = 0) override;
|
||||||
|
std::string toJson(int depth = 0) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
#include "parser/nodes/program.hpp"
|
||||||
|
#include "parser/ast_json.hpp"
|
||||||
|
|
||||||
|
ProgramNode::ProgramNode() {
|
||||||
|
kind = ASTKind::Program;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProgramNode::log(int indent) {
|
||||||
|
std::string in = jsonIndent(indent);
|
||||||
|
std::cout << in << "Program\n";
|
||||||
|
for (auto* child : children) {
|
||||||
|
child->log(indent + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string ProgramNode::toJson(int depth) {
|
||||||
|
JsonObject obj(depth);
|
||||||
|
obj.add("kind", "Program");
|
||||||
|
obj.addArray("children", [&]() {
|
||||||
|
for (auto* child : children) {
|
||||||
|
obj.addItem(child->toJson(depth + 2));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
obj.addRaw("location", loc.toJson());
|
||||||
|
return obj.str();
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
#ifndef SAQUT_AST_PROGRAM
|
||||||
|
#define SAQUT_AST_PROGRAM
|
||||||
|
|
||||||
|
#include "parser/ast_node.hpp"
|
||||||
|
|
||||||
|
class ProgramNode : public ASTNode {
|
||||||
|
public:
|
||||||
|
ProgramNode();
|
||||||
|
void log(int indent = 0) override;
|
||||||
|
std::string toJson(int depth = 0) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -0,0 +1,149 @@
|
||||||
|
#include "parser/nodes/statements.hpp"
|
||||||
|
#include "parser/ast_json.hpp"
|
||||||
|
|
||||||
|
// BlockNode
|
||||||
|
BlockNode::BlockNode() { kind = ASTKind::Block; }
|
||||||
|
void BlockNode::log(int indent) {
|
||||||
|
std::cout << jsonIndent(indent) << "Block\n";
|
||||||
|
for (auto* child : children) child->log(indent + 1);
|
||||||
|
}
|
||||||
|
std::string BlockNode::toJson(int depth) {
|
||||||
|
JsonObject obj(depth);
|
||||||
|
obj.add("kind", "Block");
|
||||||
|
obj.addArray("children", [&]() {
|
||||||
|
for (auto* child : children) obj.addItem(child->toJson(depth + 2));
|
||||||
|
});
|
||||||
|
obj.add("isReachable", isReachable);
|
||||||
|
obj.addRaw("location", loc.toJson());
|
||||||
|
return obj.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
// IfStatementNode
|
||||||
|
IfStatementNode::IfStatementNode() { kind = ASTKind::IfStatement; }
|
||||||
|
void IfStatementNode::log(int indent) {
|
||||||
|
std::cout << jsonIndent(indent) << "IfStatement\n";
|
||||||
|
if (condition) condition->log(indent + 1);
|
||||||
|
if (thenBranch) thenBranch->log(indent + 1);
|
||||||
|
if (elseBranch) elseBranch->log(indent + 1);
|
||||||
|
}
|
||||||
|
std::string IfStatementNode::toJson(int depth) {
|
||||||
|
JsonObject obj(depth);
|
||||||
|
obj.add("kind", "IfStatement");
|
||||||
|
if (condition) obj.addRaw("condition", condition->toJson(depth + 1));
|
||||||
|
if (thenBranch) obj.addRaw("then", thenBranch->toJson(depth + 1));
|
||||||
|
if (elseBranch) obj.addRaw("else", elseBranch->toJson(depth + 1));
|
||||||
|
obj.add("isReachable", isReachable);
|
||||||
|
obj.addRaw("location", loc.toJson());
|
||||||
|
return obj.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
// WhileStatementNode
|
||||||
|
WhileStatementNode::WhileStatementNode() { kind = ASTKind::WhileStatement; }
|
||||||
|
void WhileStatementNode::log(int indent) {
|
||||||
|
std::cout << jsonIndent(indent) << "WhileStatement\n";
|
||||||
|
if (condition) condition->log(indent + 1);
|
||||||
|
if (body) body->log(indent + 1);
|
||||||
|
}
|
||||||
|
std::string WhileStatementNode::toJson(int depth) {
|
||||||
|
JsonObject obj(depth);
|
||||||
|
obj.add("kind", "WhileStatement");
|
||||||
|
if (condition) obj.addRaw("condition", condition->toJson(depth + 1));
|
||||||
|
if (body) obj.addRaw("body", body->toJson(depth + 1));
|
||||||
|
obj.add("isReachable", isReachable);
|
||||||
|
obj.addRaw("location", loc.toJson());
|
||||||
|
return obj.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ForStatementNode
|
||||||
|
ForStatementNode::ForStatementNode() { kind = ASTKind::ForStatement; }
|
||||||
|
void ForStatementNode::log(int indent) {
|
||||||
|
std::cout << jsonIndent(indent) << "ForStatement\n";
|
||||||
|
if (init) init->log(indent + 1);
|
||||||
|
if (condition) condition->log(indent + 1);
|
||||||
|
if (update) update->log(indent + 1);
|
||||||
|
if (body) body->log(indent + 1);
|
||||||
|
}
|
||||||
|
std::string ForStatementNode::toJson(int depth) {
|
||||||
|
JsonObject obj(depth);
|
||||||
|
obj.add("kind", "ForStatement");
|
||||||
|
if (init) obj.addRaw("init", init->toJson(depth + 1));
|
||||||
|
if (condition) obj.addRaw("condition", condition->toJson(depth + 1));
|
||||||
|
if (update) obj.addRaw("update", update->toJson(depth + 1));
|
||||||
|
if (body) obj.addRaw("body", body->toJson(depth + 1));
|
||||||
|
obj.add("isReachable", isReachable);
|
||||||
|
obj.addRaw("location", loc.toJson());
|
||||||
|
return obj.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
// DoWhileStatementNode
|
||||||
|
DoWhileStatementNode::DoWhileStatementNode() { kind = ASTKind::DoWhileStatement; }
|
||||||
|
void DoWhileStatementNode::log(int indent) {
|
||||||
|
std::cout << jsonIndent(indent) << "DoWhileStatement\n";
|
||||||
|
if (body) body->log(indent + 1);
|
||||||
|
if (condition) condition->log(indent + 1);
|
||||||
|
}
|
||||||
|
std::string DoWhileStatementNode::toJson(int depth) {
|
||||||
|
JsonObject obj(depth);
|
||||||
|
obj.add("kind", "DoWhileStatement");
|
||||||
|
if (condition) obj.addRaw("condition", condition->toJson(depth + 1));
|
||||||
|
if (body) obj.addRaw("body", body->toJson(depth + 1));
|
||||||
|
obj.add("isReachable", isReachable);
|
||||||
|
obj.addRaw("location", loc.toJson());
|
||||||
|
return obj.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReturnStatementNode
|
||||||
|
ReturnStatementNode::ReturnStatementNode() { kind = ASTKind::ReturnStatement; }
|
||||||
|
void ReturnStatementNode::log(int indent) {
|
||||||
|
std::cout << jsonIndent(indent) << "ReturnStatement\n";
|
||||||
|
if (value) value->log(indent + 1);
|
||||||
|
}
|
||||||
|
std::string ReturnStatementNode::toJson(int depth) {
|
||||||
|
JsonObject obj(depth);
|
||||||
|
obj.add("kind", "ReturnStatement");
|
||||||
|
if (value) obj.addRaw("value", value->toJson(depth + 1));
|
||||||
|
obj.add("isReachable", isReachable);
|
||||||
|
obj.addRaw("location", loc.toJson());
|
||||||
|
return obj.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
// BreakStatementNode
|
||||||
|
BreakStatementNode::BreakStatementNode() { kind = ASTKind::BreakStatement; }
|
||||||
|
void BreakStatementNode::log(int indent) {
|
||||||
|
std::cout << jsonIndent(indent) << "BreakStatement\n";
|
||||||
|
}
|
||||||
|
std::string BreakStatementNode::toJson(int depth) {
|
||||||
|
JsonObject obj(depth);
|
||||||
|
obj.add("kind", "BreakStatement");
|
||||||
|
obj.add("isReachable", isReachable);
|
||||||
|
obj.addRaw("location", loc.toJson());
|
||||||
|
return obj.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ContinueStatementNode
|
||||||
|
ContinueStatementNode::ContinueStatementNode() { kind = ASTKind::ContinueStatement; }
|
||||||
|
void ContinueStatementNode::log(int indent) {
|
||||||
|
std::cout << jsonIndent(indent) << "ContinueStatement\n";
|
||||||
|
}
|
||||||
|
std::string ContinueStatementNode::toJson(int depth) {
|
||||||
|
JsonObject obj(depth);
|
||||||
|
obj.add("kind", "ContinueStatement");
|
||||||
|
obj.add("isReachable", isReachable);
|
||||||
|
obj.addRaw("location", loc.toJson());
|
||||||
|
return obj.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExpressionStatementNode
|
||||||
|
ExpressionStatementNode::ExpressionStatementNode() { kind = ASTKind::ExpressionStatement; }
|
||||||
|
void ExpressionStatementNode::log(int indent) {
|
||||||
|
std::cout << jsonIndent(indent) << "ExpressionStatement\n";
|
||||||
|
if (expression) expression->log(indent + 1);
|
||||||
|
}
|
||||||
|
std::string ExpressionStatementNode::toJson(int depth) {
|
||||||
|
JsonObject obj(depth);
|
||||||
|
obj.add("kind", "ExpressionStatement");
|
||||||
|
if (expression) obj.addRaw("expression", expression->toJson(depth + 1));
|
||||||
|
obj.add("isReachable", isReachable);
|
||||||
|
obj.addRaw("location", loc.toJson());
|
||||||
|
return obj.str();
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,82 @@
|
||||||
|
#ifndef SAQUT_AST_STMT
|
||||||
|
#define SAQUT_AST_STMT
|
||||||
|
|
||||||
|
#include "parser/ast_node.hpp"
|
||||||
|
|
||||||
|
class BlockNode : public StatementNode {
|
||||||
|
public:
|
||||||
|
BlockNode();
|
||||||
|
void log(int indent = 0) override;
|
||||||
|
std::string toJson(int depth = 0) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class IfStatementNode : public StatementNode {
|
||||||
|
public:
|
||||||
|
ASTNode* condition = nullptr;
|
||||||
|
ASTNode* thenBranch = nullptr;
|
||||||
|
ASTNode* elseBranch = nullptr;
|
||||||
|
IfStatementNode();
|
||||||
|
void log(int indent = 0) override;
|
||||||
|
std::string toJson(int depth = 0) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class WhileStatementNode : public StatementNode {
|
||||||
|
public:
|
||||||
|
ASTNode* condition = nullptr;
|
||||||
|
ASTNode* body = nullptr;
|
||||||
|
WhileStatementNode();
|
||||||
|
void log(int indent = 0) override;
|
||||||
|
std::string toJson(int depth = 0) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ForStatementNode : public StatementNode {
|
||||||
|
public:
|
||||||
|
ASTNode* init = nullptr;
|
||||||
|
ASTNode* condition = nullptr;
|
||||||
|
ASTNode* update = nullptr;
|
||||||
|
ASTNode* body = nullptr;
|
||||||
|
ForStatementNode();
|
||||||
|
void log(int indent = 0) override;
|
||||||
|
std::string toJson(int depth = 0) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class DoWhileStatementNode : public StatementNode {
|
||||||
|
public:
|
||||||
|
ASTNode* condition = nullptr;
|
||||||
|
ASTNode* body = nullptr;
|
||||||
|
DoWhileStatementNode();
|
||||||
|
void log(int indent = 0) override;
|
||||||
|
std::string toJson(int depth = 0) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ReturnStatementNode : public StatementNode {
|
||||||
|
public:
|
||||||
|
ASTNode* value = nullptr;
|
||||||
|
ReturnStatementNode();
|
||||||
|
void log(int indent = 0) override;
|
||||||
|
std::string toJson(int depth = 0) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class BreakStatementNode : public StatementNode {
|
||||||
|
public:
|
||||||
|
BreakStatementNode();
|
||||||
|
void log(int indent = 0) override;
|
||||||
|
std::string toJson(int depth = 0) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ContinueStatementNode : public StatementNode {
|
||||||
|
public:
|
||||||
|
ContinueStatementNode();
|
||||||
|
void log(int indent = 0) override;
|
||||||
|
std::string toJson(int depth = 0) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ExpressionStatementNode : public StatementNode {
|
||||||
|
public:
|
||||||
|
ASTNode* expression = nullptr;
|
||||||
|
ExpressionStatementNode();
|
||||||
|
void log(int indent = 0) override;
|
||||||
|
std::string toJson(int depth = 0) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -0,0 +1,629 @@
|
||||||
|
#include "parser/parser.hpp"
|
||||||
|
#include "parser/nodes/program.hpp"
|
||||||
|
#include "parser/nodes/binary_expr.hpp"
|
||||||
|
#include "parser/nodes/literal.hpp"
|
||||||
|
#include "parser/nodes/identifier.hpp"
|
||||||
|
#include "parser/nodes/expressions.hpp"
|
||||||
|
#include "parser/nodes/statements.hpp"
|
||||||
|
#include "parser/nodes/declarations.hpp"
|
||||||
|
|
||||||
|
// --------------------------------------------------------------------------
|
||||||
|
// parseToken: Ham Token'ı ParserToken'a dönüştür.
|
||||||
|
// --------------------------------------------------------------------------
|
||||||
|
ParserToken Parser::parseToken(Token* token) {
|
||||||
|
ParserToken pt;
|
||||||
|
pt.token = token;
|
||||||
|
|
||||||
|
std::string t = token->gettype();
|
||||||
|
if (t == "string")
|
||||||
|
pt.type = TokenType::STRING;
|
||||||
|
else if (t == "number")
|
||||||
|
pt.type = TokenType::NUMBER;
|
||||||
|
else if (t == "operator")
|
||||||
|
pt.type = OPERATOR_MAP.find(pt.token->token)->second;
|
||||||
|
else if (t == "delimiter")
|
||||||
|
pt.type = OPERATOR_MAP.find(pt.token->token)->second;
|
||||||
|
else if (t == "keyword")
|
||||||
|
pt.type = KEYWORD_MAP.find(pt.token->token)->second;
|
||||||
|
else if (t == "identifier")
|
||||||
|
pt.type = TokenType::IDENTIFIER;
|
||||||
|
|
||||||
|
return pt;
|
||||||
|
}
|
||||||
|
|
||||||
|
ParserToken Parser::getToken(int offset) {
|
||||||
|
if ((int)tokens.size() - 1 < current + offset) {
|
||||||
|
ParserToken pt;
|
||||||
|
pt.type = TokenType::SVR_VOID;
|
||||||
|
return pt;
|
||||||
|
}
|
||||||
|
return parseToken(tokens[current + offset]);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Parser::nextToken() {
|
||||||
|
if ((int)tokens.size() >= current + 1)
|
||||||
|
current++;
|
||||||
|
}
|
||||||
|
|
||||||
|
ParserToken Parser::lookahead(uint32_t forward) {
|
||||||
|
return getToken(forward);
|
||||||
|
}
|
||||||
|
|
||||||
|
ParserToken Parser::currentToken() {
|
||||||
|
return getToken(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
ASTNode* Parser::parse(TokenList toks) {
|
||||||
|
tokens = toks;
|
||||||
|
current = 0;
|
||||||
|
return parseProgram();
|
||||||
|
}
|
||||||
|
|
||||||
|
ASTNode* Parser::parseProgram() {
|
||||||
|
ProgramNode* program = new ProgramNode();
|
||||||
|
|
||||||
|
while (currentToken().type != TokenType::SVR_VOID) {
|
||||||
|
int prevPos = current;
|
||||||
|
ASTNode* decl = parseDeclaration();
|
||||||
|
if (decl)
|
||||||
|
program->addChild(decl);
|
||||||
|
// İlerleme olmadıysa token atla — syntax hatasında sonsuz döngüyü önler
|
||||||
|
if (current == prevPos)
|
||||||
|
nextToken();
|
||||||
|
}
|
||||||
|
|
||||||
|
return program;
|
||||||
|
}
|
||||||
|
|
||||||
|
ASTNode* Parser::parseDeclaration() {
|
||||||
|
auto ct = currentToken();
|
||||||
|
|
||||||
|
if (ct.is({
|
||||||
|
TokenType::KW_VOID, TokenType::KW_INT, TokenType::KW_FLOAT_TYPE,
|
||||||
|
TokenType::KW_DOUBLE, TokenType::KW_BOOL, TokenType::KW_CHAR,
|
||||||
|
TokenType::KW_STRING_TYPE, TokenType::KW_AUTO
|
||||||
|
})) {
|
||||||
|
auto la1 = lookahead(1);
|
||||||
|
auto la2 = lookahead(2);
|
||||||
|
if (la1.type == TokenType::IDENTIFIER && la2.type == TokenType::LPAREN)
|
||||||
|
return parseFunctionDecl();
|
||||||
|
return parseVariableDecl();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ct.type == TokenType::KW_STRUCT)
|
||||||
|
return parseStructDecl();
|
||||||
|
|
||||||
|
// Kullanıcı tanımlı tip adı (struct tipi) ile değişken/fonksiyon bildirimi
|
||||||
|
if (ct.type == TokenType::IDENTIFIER) {
|
||||||
|
auto la1 = lookahead(1);
|
||||||
|
auto la2 = lookahead(2);
|
||||||
|
if (la1.type == TokenType::IDENTIFIER && la2.type == TokenType::LPAREN)
|
||||||
|
return parseFunctionDecl();
|
||||||
|
if (la1.type == TokenType::IDENTIFIER)
|
||||||
|
return parseVariableDecl();
|
||||||
|
}
|
||||||
|
|
||||||
|
return parseStatement();
|
||||||
|
}
|
||||||
|
|
||||||
|
ASTNode* Parser::parseExpression() {
|
||||||
|
return parseExpression(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
ASTNode* Parser::parseExpression(uint16_t precedence) {
|
||||||
|
if (currentToken().type == TokenType::SVR_VOID)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
ASTNode* left = parseNullDenotation();
|
||||||
|
if (!left) return nullptr;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
auto next = currentToken();
|
||||||
|
if (next.type == TokenType::RPAREN ||
|
||||||
|
next.type == TokenType::SEMICOLON ||
|
||||||
|
next.type == TokenType::RBRACE ||
|
||||||
|
next.type == TokenType::COMMA)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (precedence < next.getPowerOperator()) {
|
||||||
|
left = parseLeftDenotation(left);
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return left;
|
||||||
|
}
|
||||||
|
|
||||||
|
ASTNode* Parser::parseNullDenotation() {
|
||||||
|
auto ct = currentToken();
|
||||||
|
|
||||||
|
if (ct.type == TokenType::SVR_VOID) {
|
||||||
|
std::cerr << "Parser hatası: beklenmeyen dosya sonu\n";
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ct.type == TokenType::LPAREN) {
|
||||||
|
nextToken();
|
||||||
|
ASTNode* expr = parseExpression(0);
|
||||||
|
if (currentToken().type == TokenType::RPAREN)
|
||||||
|
nextToken();
|
||||||
|
return expr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ct.is({
|
||||||
|
TokenType::PLUS_PLUS, TokenType::MINUS_MINUS,
|
||||||
|
TokenType::PLUS, TokenType::MINUS,
|
||||||
|
TokenType::BANG, TokenType::TILDE
|
||||||
|
})) {
|
||||||
|
nextToken();
|
||||||
|
ASTNode* right = parseExpression(ct.getPowerOperator());
|
||||||
|
BinaryExpressionNode* bin = new BinaryExpressionNode();
|
||||||
|
bin->loc = ct.token ? ct.token->loc : SourceLocation{};
|
||||||
|
bin->Right = right;
|
||||||
|
bin->Left = nullptr;
|
||||||
|
bin->Operator = ct.type;
|
||||||
|
if (right) right->parent = bin;
|
||||||
|
return bin;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ct.type == TokenType::NUMBER) {
|
||||||
|
nextToken();
|
||||||
|
LiteralNode* lit = new LiteralNode();
|
||||||
|
lit->loc = ct.token ? ct.token->loc : SourceLocation{};
|
||||||
|
lit->lexerToken = ct.token;
|
||||||
|
lit->parserToken = ct;
|
||||||
|
if (auto* nt = dynamic_cast<NumberToken*>(ct.token)) {
|
||||||
|
lit->literalBase = nt->base;
|
||||||
|
lit->isFloatValue = nt->isFloat;
|
||||||
|
lit->literalType = nt->isFloat ? LiteralType::FLOAT : LiteralType::INTEGER;
|
||||||
|
}
|
||||||
|
return lit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ct.type == TokenType::STRING) {
|
||||||
|
nextToken();
|
||||||
|
LiteralNode* lit = new LiteralNode();
|
||||||
|
lit->literalType = LiteralType::STRING;
|
||||||
|
lit->loc = ct.token ? ct.token->loc : SourceLocation{};
|
||||||
|
lit->lexerToken = ct.token;
|
||||||
|
lit->parserToken = ct;
|
||||||
|
return lit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ct.is({TokenType::KW_TRUE, TokenType::KW_FALSE, TokenType::KW_NULL})) {
|
||||||
|
nextToken();
|
||||||
|
LiteralNode* lit = new LiteralNode();
|
||||||
|
if (ct.is({TokenType::KW_TRUE, TokenType::KW_FALSE}))
|
||||||
|
lit->literalType = LiteralType::BOOLEAN;
|
||||||
|
else
|
||||||
|
lit->literalType = LiteralType::BOŞ;
|
||||||
|
lit->loc = ct.token ? ct.token->loc : SourceLocation{};
|
||||||
|
lit->lexerToken = ct.token;
|
||||||
|
lit->parserToken = ct;
|
||||||
|
return lit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ct.type == TokenType::IDENTIFIER) {
|
||||||
|
nextToken();
|
||||||
|
IdentifierNode* id = new IdentifierNode();
|
||||||
|
id->loc = ct.token ? ct.token->loc : SourceLocation{};
|
||||||
|
id->lexerToken = ct.token;
|
||||||
|
id->parserToken = ct;
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
ASTNode* Parser::parseLeftDenotation(ASTNode* left) {
|
||||||
|
auto ct = currentToken();
|
||||||
|
|
||||||
|
if (ct.is({TokenType::PLUS_PLUS, TokenType::MINUS_MINUS})) {
|
||||||
|
nextToken();
|
||||||
|
PostfixNode* pf = new PostfixNode();
|
||||||
|
pf->loc = ct.token ? ct.token->loc : SourceLocation{};
|
||||||
|
pf->operand = left;
|
||||||
|
pf->Operator = ct.type;
|
||||||
|
left->parent = pf;
|
||||||
|
return pf;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ct.type == TokenType::LPAREN) {
|
||||||
|
nextToken();
|
||||||
|
CallExpressionNode* call = new CallExpressionNode();
|
||||||
|
call->loc = ct.token ? ct.token->loc : SourceLocation{};
|
||||||
|
call->callee = left;
|
||||||
|
left->parent = call;
|
||||||
|
|
||||||
|
if (currentToken().type != TokenType::RPAREN) {
|
||||||
|
call->arguments.push_back(parseExpression(0));
|
||||||
|
while (currentToken().type == TokenType::COMMA) {
|
||||||
|
nextToken();
|
||||||
|
call->arguments.push_back(parseExpression(0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (currentToken().type == TokenType::RPAREN)
|
||||||
|
nextToken();
|
||||||
|
return call;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ct.type == TokenType::LBRACKET) {
|
||||||
|
nextToken();
|
||||||
|
IndexExpressionNode* idx = new IndexExpressionNode();
|
||||||
|
idx->loc = ct.token ? ct.token->loc : SourceLocation{};
|
||||||
|
idx->object = left;
|
||||||
|
left->parent = idx;
|
||||||
|
idx->index = parseExpression(0);
|
||||||
|
if (currentToken().type == TokenType::RBRACKET)
|
||||||
|
nextToken();
|
||||||
|
return idx;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ct.type == TokenType::DOT || ct.type == TokenType::ARROW) {
|
||||||
|
bool arrow = (ct.type == TokenType::ARROW);
|
||||||
|
nextToken();
|
||||||
|
|
||||||
|
if (currentToken().type != TokenType::IDENTIFIER) {
|
||||||
|
std::cerr << "Parser hatasi: uye ismi bekleniyor\n";
|
||||||
|
return left;
|
||||||
|
}
|
||||||
|
|
||||||
|
MemberAccessNode* ma = new MemberAccessNode();
|
||||||
|
ma->loc = ct.token ? ct.token->loc : SourceLocation{};
|
||||||
|
ma->object = left;
|
||||||
|
ma->member = currentToken().token->token;
|
||||||
|
ma->arrow = arrow;
|
||||||
|
left->parent = ma;
|
||||||
|
nextToken();
|
||||||
|
return ma;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t prec = ct.getPowerOperator();
|
||||||
|
nextToken();
|
||||||
|
|
||||||
|
ASTNode* right = parseExpression(prec);
|
||||||
|
|
||||||
|
BinaryExpressionNode* bin = new BinaryExpressionNode();
|
||||||
|
bin->loc = ct.token ? ct.token->loc : SourceLocation{};
|
||||||
|
bin->Left = left;
|
||||||
|
bin->Right = right;
|
||||||
|
bin->Operator = ct.type;
|
||||||
|
if (left) left->parent = bin;
|
||||||
|
if (right) right->parent = bin;
|
||||||
|
return bin;
|
||||||
|
}
|
||||||
|
|
||||||
|
ASTNode* Parser::parseFunctionDecl() {
|
||||||
|
FunctionDeclNode* fn = new FunctionDeclNode();
|
||||||
|
fn->loc = currentToken().token->loc;
|
||||||
|
fn->returnType = currentToken().token->token;
|
||||||
|
nextToken();
|
||||||
|
|
||||||
|
fn->name = currentToken().token->token;
|
||||||
|
nextToken();
|
||||||
|
|
||||||
|
if (currentToken().type == TokenType::LPAREN) {
|
||||||
|
nextToken();
|
||||||
|
while (currentToken().type != TokenType::RPAREN &&
|
||||||
|
currentToken().type != TokenType::SVR_VOID) {
|
||||||
|
auto typeTok = currentToken();
|
||||||
|
bool isTypeKw = typeTok.is({
|
||||||
|
TokenType::KW_VOID, TokenType::KW_INT, TokenType::KW_FLOAT_TYPE,
|
||||||
|
TokenType::KW_DOUBLE, TokenType::KW_BOOL, TokenType::KW_CHAR,
|
||||||
|
TokenType::KW_STRING_TYPE, TokenType::KW_AUTO
|
||||||
|
}) || typeTok.type == TokenType::IDENTIFIER;
|
||||||
|
if (!isTypeKw || !typeTok.token) break;
|
||||||
|
std::string paramType = typeTok.token->token;
|
||||||
|
nextToken();
|
||||||
|
if (currentToken().type != TokenType::IDENTIFIER || !currentToken().token) break;
|
||||||
|
VariableDeclNode* param = new VariableDeclNode();
|
||||||
|
param->loc = currentToken().token->loc;
|
||||||
|
param->varType = paramType;
|
||||||
|
param->name = currentToken().token->token;
|
||||||
|
nextToken();
|
||||||
|
fn->params.push_back(param);
|
||||||
|
if (currentToken().type == TokenType::COMMA)
|
||||||
|
nextToken();
|
||||||
|
}
|
||||||
|
if (currentToken().type == TokenType::RPAREN)
|
||||||
|
nextToken();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentToken().type == TokenType::LBRACE) {
|
||||||
|
ASTNode* body = parseBlock();
|
||||||
|
fn->addChild(body);
|
||||||
|
}
|
||||||
|
|
||||||
|
return fn;
|
||||||
|
}
|
||||||
|
|
||||||
|
ASTNode* Parser::parseStructDecl() {
|
||||||
|
StructDeclNode* st = new StructDeclNode();
|
||||||
|
st->loc = currentToken().token->loc;
|
||||||
|
nextToken();
|
||||||
|
if (currentToken().type == TokenType::IDENTIFIER) {
|
||||||
|
st->name = currentToken().token->token;
|
||||||
|
nextToken();
|
||||||
|
}
|
||||||
|
if (currentToken().type == TokenType::LBRACE) {
|
||||||
|
nextToken();
|
||||||
|
while (currentToken().type != TokenType::RBRACE && currentToken().type != TokenType::SVR_VOID) {
|
||||||
|
ASTNode* field = parseDeclaration();
|
||||||
|
if (field) st->addChild(field);
|
||||||
|
else break;
|
||||||
|
}
|
||||||
|
if (currentToken().type == TokenType::RBRACE) nextToken();
|
||||||
|
}
|
||||||
|
if (currentToken().type == TokenType::SEMICOLON) nextToken();
|
||||||
|
return st;
|
||||||
|
}
|
||||||
|
|
||||||
|
ASTNode* Parser::parseVariableDecl() {
|
||||||
|
VariableDeclNode* vd = new VariableDeclNode();
|
||||||
|
vd->loc = currentToken().token->loc;
|
||||||
|
vd->varType = currentToken().token->token;
|
||||||
|
nextToken();
|
||||||
|
|
||||||
|
if (currentToken().type != TokenType::IDENTIFIER) {
|
||||||
|
std::cerr << "Parser hatası: değişken ismi bekleniyor\n";
|
||||||
|
return vd;
|
||||||
|
}
|
||||||
|
|
||||||
|
vd->name = currentToken().token->token;
|
||||||
|
nextToken();
|
||||||
|
|
||||||
|
if (currentToken().type == TokenType::LBRACKET) {
|
||||||
|
nextToken();
|
||||||
|
while (currentToken().type != TokenType::RBRACKET &&
|
||||||
|
currentToken().type != TokenType::SEMICOLON &&
|
||||||
|
currentToken().type != TokenType::SVR_VOID)
|
||||||
|
nextToken();
|
||||||
|
if (currentToken().type == TokenType::RBRACKET)
|
||||||
|
nextToken();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentToken().type == TokenType::EQUAL) {
|
||||||
|
nextToken();
|
||||||
|
vd->initExpr = parseExpression();
|
||||||
|
}
|
||||||
|
|
||||||
|
while (currentToken().type == TokenType::COMMA) {
|
||||||
|
nextToken();
|
||||||
|
|
||||||
|
if (currentToken().type != TokenType::IDENTIFIER) {
|
||||||
|
std::cerr << "Parser hatası: virgülden sonra değişken ismi bekleniyor\n";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
VariableDeclNode* sibling = new VariableDeclNode();
|
||||||
|
sibling->loc = currentToken().token->loc;
|
||||||
|
sibling->varType = vd->varType;
|
||||||
|
sibling->name = currentToken().token->token;
|
||||||
|
nextToken();
|
||||||
|
|
||||||
|
if (currentToken().type == TokenType::LBRACKET) {
|
||||||
|
nextToken();
|
||||||
|
while (currentToken().type != TokenType::RBRACKET &&
|
||||||
|
currentToken().type != TokenType::SEMICOLON &&
|
||||||
|
currentToken().type != TokenType::SVR_VOID)
|
||||||
|
nextToken();
|
||||||
|
if (currentToken().type == TokenType::RBRACKET)
|
||||||
|
nextToken();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentToken().type == TokenType::EQUAL) {
|
||||||
|
nextToken();
|
||||||
|
sibling->initExpr = parseExpression();
|
||||||
|
}
|
||||||
|
|
||||||
|
vd->addChild(sibling);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentToken().type == TokenType::SEMICOLON)
|
||||||
|
nextToken();
|
||||||
|
|
||||||
|
return vd;
|
||||||
|
}
|
||||||
|
|
||||||
|
ASTNode* Parser::parseStatement() {
|
||||||
|
auto ct = currentToken();
|
||||||
|
|
||||||
|
if (ct.type == TokenType::LBRACE)
|
||||||
|
return parseBlock();
|
||||||
|
|
||||||
|
if (ct.type == TokenType::KW_IF)
|
||||||
|
return parseIfStatement();
|
||||||
|
|
||||||
|
if (ct.type == TokenType::KW_WHILE)
|
||||||
|
return parseWhileStatement();
|
||||||
|
|
||||||
|
if (ct.type == TokenType::KW_FOR)
|
||||||
|
return parseForStatement();
|
||||||
|
|
||||||
|
if (ct.type == TokenType::KW_DO)
|
||||||
|
return parseDoWhileStatement();
|
||||||
|
|
||||||
|
if (ct.type == TokenType::KW_RETURN)
|
||||||
|
return parseReturnStatement();
|
||||||
|
|
||||||
|
if (ct.type == TokenType::KW_BREAK)
|
||||||
|
return parseBreakStatement();
|
||||||
|
|
||||||
|
if (ct.type == TokenType::KW_CONTINUE)
|
||||||
|
return parseContinueStatement();
|
||||||
|
|
||||||
|
if (ct.is({
|
||||||
|
TokenType::KW_VOID, TokenType::KW_INT, TokenType::KW_FLOAT_TYPE,
|
||||||
|
TokenType::KW_DOUBLE, TokenType::KW_BOOL, TokenType::KW_CHAR,
|
||||||
|
TokenType::KW_STRING_TYPE
|
||||||
|
})) {
|
||||||
|
return parseVariableDecl();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ct.type == TokenType::KW_STRUCT)
|
||||||
|
return parseStructDecl();
|
||||||
|
|
||||||
|
return parseExpressionStatement();
|
||||||
|
}
|
||||||
|
|
||||||
|
ASTNode* Parser::parseBlock() {
|
||||||
|
BlockNode* block = new BlockNode();
|
||||||
|
block->loc = currentToken().token ? currentToken().token->loc : SourceLocation{};
|
||||||
|
|
||||||
|
if (currentToken().type == TokenType::LBRACE)
|
||||||
|
nextToken();
|
||||||
|
|
||||||
|
while (currentToken().type != TokenType::RBRACE &&
|
||||||
|
currentToken().type != TokenType::SVR_VOID) {
|
||||||
|
ASTNode* stmt = parseStatement();
|
||||||
|
if (stmt)
|
||||||
|
block->addChild(stmt);
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentToken().type == TokenType::RBRACE)
|
||||||
|
nextToken();
|
||||||
|
|
||||||
|
return block;
|
||||||
|
}
|
||||||
|
|
||||||
|
ASTNode* Parser::parseIfStatement() {
|
||||||
|
IfStatementNode* ifNode = new IfStatementNode();
|
||||||
|
ifNode->loc = currentToken().token->loc;
|
||||||
|
nextToken();
|
||||||
|
|
||||||
|
if (currentToken().type == TokenType::LPAREN) {
|
||||||
|
nextToken();
|
||||||
|
ifNode->condition = parseExpression();
|
||||||
|
if (currentToken().type == TokenType::RPAREN)
|
||||||
|
nextToken();
|
||||||
|
}
|
||||||
|
|
||||||
|
ifNode->thenBranch = parseStatement();
|
||||||
|
|
||||||
|
if (currentToken().type == TokenType::KW_ELSE) {
|
||||||
|
nextToken();
|
||||||
|
ifNode->elseBranch = parseStatement();
|
||||||
|
}
|
||||||
|
|
||||||
|
return ifNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
ASTNode* Parser::parseWhileStatement() {
|
||||||
|
WhileStatementNode* ws = new WhileStatementNode();
|
||||||
|
ws->loc = currentToken().token->loc;
|
||||||
|
nextToken();
|
||||||
|
|
||||||
|
if (currentToken().type == TokenType::LPAREN) {
|
||||||
|
nextToken();
|
||||||
|
ws->condition = parseExpression();
|
||||||
|
if (currentToken().type == TokenType::RPAREN)
|
||||||
|
nextToken();
|
||||||
|
}
|
||||||
|
|
||||||
|
ws->body = parseStatement();
|
||||||
|
return ws;
|
||||||
|
}
|
||||||
|
|
||||||
|
ASTNode* Parser::parseForStatement() {
|
||||||
|
ForStatementNode* fs = new ForStatementNode();
|
||||||
|
fs->loc = currentToken().token->loc;
|
||||||
|
nextToken();
|
||||||
|
|
||||||
|
if (currentToken().type == TokenType::LPAREN)
|
||||||
|
nextToken();
|
||||||
|
|
||||||
|
if (currentToken().type != TokenType::SEMICOLON)
|
||||||
|
fs->init = parseStatement();
|
||||||
|
if (currentToken().type == TokenType::SEMICOLON)
|
||||||
|
nextToken();
|
||||||
|
|
||||||
|
if (currentToken().type != TokenType::SEMICOLON)
|
||||||
|
fs->condition = parseExpression();
|
||||||
|
if (currentToken().type == TokenType::SEMICOLON)
|
||||||
|
nextToken();
|
||||||
|
|
||||||
|
if (currentToken().type != TokenType::RPAREN)
|
||||||
|
fs->update = parseExpression();
|
||||||
|
if (currentToken().type == TokenType::RPAREN)
|
||||||
|
nextToken();
|
||||||
|
|
||||||
|
fs->body = parseStatement();
|
||||||
|
|
||||||
|
return fs;
|
||||||
|
}
|
||||||
|
|
||||||
|
ASTNode* Parser::parseDoWhileStatement() {
|
||||||
|
DoWhileStatementNode* dw = new DoWhileStatementNode();
|
||||||
|
dw->loc = currentToken().token->loc;
|
||||||
|
nextToken();
|
||||||
|
|
||||||
|
dw->body = parseStatement();
|
||||||
|
|
||||||
|
if (currentToken().type == TokenType::KW_WHILE) {
|
||||||
|
nextToken();
|
||||||
|
if (currentToken().type == TokenType::LPAREN) {
|
||||||
|
nextToken();
|
||||||
|
dw->condition = parseExpression();
|
||||||
|
if (currentToken().type == TokenType::RPAREN)
|
||||||
|
nextToken();
|
||||||
|
}
|
||||||
|
if (currentToken().type == TokenType::SEMICOLON)
|
||||||
|
nextToken();
|
||||||
|
}
|
||||||
|
|
||||||
|
return dw;
|
||||||
|
}
|
||||||
|
|
||||||
|
ASTNode* Parser::parseReturnStatement() {
|
||||||
|
ReturnStatementNode* rs = new ReturnStatementNode();
|
||||||
|
rs->loc = currentToken().token->loc;
|
||||||
|
nextToken();
|
||||||
|
|
||||||
|
if (currentToken().type != TokenType::SEMICOLON &&
|
||||||
|
currentToken().type != TokenType::RBRACE) {
|
||||||
|
rs->value = parseExpression();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentToken().type == TokenType::SEMICOLON)
|
||||||
|
nextToken();
|
||||||
|
|
||||||
|
return rs;
|
||||||
|
}
|
||||||
|
|
||||||
|
ASTNode* Parser::parseBreakStatement() {
|
||||||
|
BreakStatementNode* bs = new BreakStatementNode();
|
||||||
|
bs->loc = currentToken().token->loc;
|
||||||
|
nextToken();
|
||||||
|
if (currentToken().type == TokenType::SEMICOLON)
|
||||||
|
nextToken();
|
||||||
|
return bs;
|
||||||
|
}
|
||||||
|
|
||||||
|
ASTNode* Parser::parseContinueStatement() {
|
||||||
|
ContinueStatementNode* cs = new ContinueStatementNode();
|
||||||
|
cs->loc = currentToken().token->loc;
|
||||||
|
nextToken();
|
||||||
|
if (currentToken().type == TokenType::SEMICOLON)
|
||||||
|
nextToken();
|
||||||
|
return cs;
|
||||||
|
}
|
||||||
|
|
||||||
|
ASTNode* Parser::parseExpressionStatement() {
|
||||||
|
ExpressionStatementNode* es = new ExpressionStatementNode();
|
||||||
|
es->loc = currentToken().token ? currentToken().token->loc : SourceLocation{};
|
||||||
|
es->expression = parseExpression();
|
||||||
|
if (!es->expression) {
|
||||||
|
while (currentToken().type != TokenType::SEMICOLON &&
|
||||||
|
currentToken().type != TokenType::RBRACE &&
|
||||||
|
currentToken().type != TokenType::SVR_VOID)
|
||||||
|
nextToken();
|
||||||
|
if (currentToken().type == TokenType::SEMICOLON)
|
||||||
|
nextToken();
|
||||||
|
}
|
||||||
|
if (currentToken().type == TokenType::SEMICOLON)
|
||||||
|
nextToken();
|
||||||
|
|
||||||
|
return es;
|
||||||
|
}
|
||||||
|
|
@ -1,836 +1,6 @@
|
||||||
// ============================================================================
|
|
||||||
// saQut Compiler — Parser (Sözdizimi Ayrıştırıcı)
|
|
||||||
// ============================================================================
|
|
||||||
//
|
|
||||||
// DİZİN: src/parser/parser.hpp
|
|
||||||
// KATMAN: Katman 3 — Tokenizer'ı tüketir, AST üretir
|
|
||||||
// BAĞIMLI: Token (token.hpp), AST (ast.hpp)
|
|
||||||
// KULLANAN: main.cpp
|
|
||||||
//
|
|
||||||
// AMAÇ:
|
|
||||||
// Tokenizer'ın ürettiği düz token listesini alıp, dilin gramer kurallarına
|
|
||||||
// göre hiyerarşik bir AST (Abstract Syntax Tree) üretir.
|
|
||||||
//
|
|
||||||
// İKİ AYRI PARSER STRATEJİSİ:
|
|
||||||
// 1. Recursive Descent (ifadeler için Pratt parser):
|
|
||||||
// - parseNullDenotation() (NUD): Prefix ifadeleri (sayılar, -, !, parantez)
|
|
||||||
// - parseLeftDenotation() (LED): Infix/Postfix ifadeler (+, *, ++)
|
|
||||||
// - parseExpression(precedence): Pratt'ın ana döngüsü
|
|
||||||
//
|
|
||||||
// 2. Recursive Descent (statement/deklarasyon için):
|
|
||||||
// - parseDeclaration(): Fonksiyon mu, değişken mi, statement mı?
|
|
||||||
// - parseStatement(): if/for/while/do/return/block/expression
|
|
||||||
// - Her statement tipi kendi parse fonksiyonuna sahip
|
|
||||||
//
|
|
||||||
// ADR-002 (devam): Neden Hibrit Yaklaşım?
|
|
||||||
// Pratt parser, operatör önceliğini merkezi bir tabloda yönetir ve yeni
|
|
||||||
// operatör eklemeyi kolaylaştırır. Ancak statement'lar (if, for, while)
|
|
||||||
// operatör değildir; kendi özel sözdizimleri vardır. Bu nedenle statement
|
|
||||||
// tarafında klasik recursive descent kullanıyoruz. Bu, her iki dünyanın
|
|
||||||
// en iyisini birleştirir.
|
|
||||||
//
|
|
||||||
// PARSER AKIŞI:
|
|
||||||
// parse(tokens)
|
|
||||||
// └── parseProgram()
|
|
||||||
// └── parseDeclaration() [döngü, SVR_VOID gelene kadar]
|
|
||||||
// ├── parseFunctionDecl() → tip + isim + ( ) + { gövde }
|
|
||||||
// ├── parseVariableDecl() → tip + isim [+ = ifade] + ;
|
|
||||||
// └── parseStatement()
|
|
||||||
// ├── parseBlock() → { statement* }
|
|
||||||
// ├── parseIfStatement() → if (expr) stmt [else stmt]
|
|
||||||
// ├── parseWhileStatement() → while (expr) stmt
|
|
||||||
// ├── parseForStatement() → for (stmt; expr; expr) stmt
|
|
||||||
// ├── parseDoWhileStatement() → do stmt while (expr);
|
|
||||||
// ├── parseReturnStatement() → return [expr];
|
|
||||||
// ├── parseBreakStatement() → break;
|
|
||||||
// ├── parseContinueStatement() → continue;
|
|
||||||
// ├── parseVariableDecl() → tip + isim ...
|
|
||||||
// └── parseExpressionStatement() → expr;
|
|
||||||
// └── parseExpression() [Pratt]
|
|
||||||
// ├── parseNullDenotation()
|
|
||||||
// │ ├── LPAREN → ( expr )
|
|
||||||
// │ ├── Unary prefix → !expr, -expr, ++expr
|
|
||||||
// │ ├── NUMBER → Literal
|
|
||||||
// │ ├── STRING → Literal
|
|
||||||
// │ ├── true/false/null → Literal
|
|
||||||
// │ └── IDENTIFIER → Identifier
|
|
||||||
// └── parseLeftDenotation() [döngü]
|
|
||||||
// ├── Postfix → expr++, expr--
|
|
||||||
// └── Binary infix → expr + expr
|
|
||||||
//
|
|
||||||
// BİLİNEN SINIRLAMALAR (TODO):
|
|
||||||
// TODO: else-if zincirleri (şu anda else'den sonra if gelirse düzgün çalışır mı?)
|
|
||||||
// TODO: Hata kurtarma (panic mode): ilk hatada durmak yerine senkronizasyon
|
|
||||||
// TODO: Fonksiyon parametreleri
|
|
||||||
// TODO: Dizi erişimi: a[i]
|
|
||||||
// TODO: Fonksiyon çağrısı: f(x, y)
|
|
||||||
// TODO: Üye erişimi: a.b, a->b
|
|
||||||
// TODO: Ternary: a ? b : c
|
|
||||||
// TODO: Tip kontrolü ve sembol tablosu
|
|
||||||
//
|
|
||||||
// ============================================================================
|
|
||||||
|
|
||||||
#ifndef SAQUT_PARSER
|
#ifndef SAQUT_PARSER
|
||||||
#define SAQUT_PARSER
|
#define SAQUT_PARSER
|
||||||
|
|
||||||
#include <iostream>
|
#include "parser/parser_base.hpp"
|
||||||
#include <cstdint>
|
|
||||||
#include <string>
|
|
||||||
#include "parser/token.hpp"
|
|
||||||
#include "parser/ast.hpp"
|
|
||||||
#include "tools.hpp"
|
|
||||||
|
|
||||||
// ============================================================================
|
|
||||||
// Parser — Sözdizimi Ayrıştırıcı
|
|
||||||
// ============================================================================
|
|
||||||
//
|
|
||||||
// Durum bilgisi:
|
|
||||||
// tokens: Tokenizer'dan gelen token listesi (referans değil, kopya değil)
|
|
||||||
// current: Şu anki token'ın indeksi (0 = ilk token)
|
|
||||||
//
|
|
||||||
// Token navigasyon metotları:
|
|
||||||
// currentToken(): tokens[current] döndürür, ilerlemez
|
|
||||||
// nextToken(): current++ (sonraki token'a geç)
|
|
||||||
// lookahead(n): tokens[current + n] döndürür, ilerlemez
|
|
||||||
// getToken(offset): tokens[current + offset] döndürür
|
|
||||||
//
|
|
||||||
class Parser {
|
|
||||||
public:
|
|
||||||
ASTNode* parse(TokenList tokens);
|
|
||||||
|
|
||||||
private:
|
|
||||||
TokenList tokens; // Tokenizer'dan gelen token listesi
|
|
||||||
int current = 0; // Şu anki token indeksi
|
|
||||||
|
|
||||||
// --- Token navigasyonu ---
|
|
||||||
ParserToken currentToken();
|
|
||||||
void nextToken();
|
|
||||||
ParserToken lookahead(uint32_t forward);
|
|
||||||
ParserToken parseToken(Token* token);
|
|
||||||
ParserToken getToken(int offset);
|
|
||||||
|
|
||||||
// --- Üst seviye ---
|
|
||||||
ASTNode* parseProgram();
|
|
||||||
|
|
||||||
// --- Deklarasyonlar ---
|
|
||||||
ASTNode* parseDeclaration();
|
|
||||||
ASTNode* parseFunctionDecl();
|
|
||||||
ASTNode* parseVariableDecl();
|
|
||||||
|
|
||||||
// --- Statement'lar ---
|
|
||||||
ASTNode* parseStatement();
|
|
||||||
ASTNode* parseBlock();
|
|
||||||
ASTNode* parseIfStatement();
|
|
||||||
ASTNode* parseWhileStatement();
|
|
||||||
ASTNode* parseForStatement();
|
|
||||||
ASTNode* parseDoWhileStatement();
|
|
||||||
ASTNode* parseReturnStatement();
|
|
||||||
ASTNode* parseBreakStatement();
|
|
||||||
ASTNode* parseContinueStatement();
|
|
||||||
ASTNode* parseExpressionStatement();
|
|
||||||
|
|
||||||
// --- İfadeler (Pratt parser) ---
|
|
||||||
ASTNode* parseExpression();
|
|
||||||
ASTNode* parseExpression(uint16_t precedence);
|
|
||||||
ASTNode* parseNullDenotation();
|
|
||||||
ASTNode* parseLeftDenotation(ASTNode* left);
|
|
||||||
};
|
|
||||||
|
|
||||||
// ============================================================================
|
|
||||||
// Token Navigasyonu
|
|
||||||
// ============================================================================
|
|
||||||
|
|
||||||
// --------------------------------------------------------------------------
|
|
||||||
// parseToken: Ham Token'ı ParserToken'a dönüştür.
|
|
||||||
//
|
|
||||||
// Tokenizer'ın string tabanlı tip sistemini ("number", "operator", ...)
|
|
||||||
// Parser'ın anlamsal tip sistemine (NUMBER, PLUS, KW_IF, ...) çevirir.
|
|
||||||
//
|
|
||||||
// BUG FIX (commit 40579ca): pt.token = token (pointer ataması).
|
|
||||||
// Eskiden pt.token = *token (değer kopyası) object slicing yapıyordu.
|
|
||||||
// --------------------------------------------------------------------------
|
|
||||||
inline ParserToken Parser::parseToken(Token* token) {
|
|
||||||
ParserToken pt;
|
|
||||||
pt.token = token; // Pointer — değer kopyası DEĞİL
|
|
||||||
|
|
||||||
std::string t = token->gettype();
|
|
||||||
if (t == "string")
|
|
||||||
pt.type = TokenType::STRING;
|
|
||||||
else if (t == "number")
|
|
||||||
pt.type = TokenType::NUMBER;
|
|
||||||
else if (t == "operator")
|
|
||||||
pt.type = OPERATOR_MAP.find(pt.token->token)->second;
|
|
||||||
else if (t == "delimiter")
|
|
||||||
pt.type = OPERATOR_MAP.find(pt.token->token)->second;
|
|
||||||
else if (t == "keyword")
|
|
||||||
pt.type = KEYWORD_MAP.find(pt.token->token)->second;
|
|
||||||
else if (t == "identifier")
|
|
||||||
pt.type = TokenType::IDENTIFIER;
|
|
||||||
|
|
||||||
return pt;
|
|
||||||
}
|
|
||||||
|
|
||||||
// --------------------------------------------------------------------------
|
|
||||||
// getToken: Güvenli token erişimi. Sınır dışı = SVR_VOID.
|
|
||||||
// --------------------------------------------------------------------------
|
|
||||||
inline ParserToken Parser::getToken(int offset) {
|
|
||||||
if ((int)tokens.size() - 1 < current + offset) {
|
|
||||||
ParserToken pt;
|
|
||||||
pt.type = TokenType::SVR_VOID;
|
|
||||||
return pt;
|
|
||||||
}
|
|
||||||
return parseToken(tokens[current + offset]);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void Parser::nextToken() {
|
|
||||||
if ((int)tokens.size() >= current + 1)
|
|
||||||
current++;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline ParserToken Parser::lookahead(uint32_t forward) {
|
|
||||||
return getToken(forward);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline ParserToken Parser::currentToken() {
|
|
||||||
return getToken(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ============================================================================
|
|
||||||
// Üst Seviye
|
|
||||||
// ============================================================================
|
|
||||||
|
|
||||||
// --------------------------------------------------------------------------
|
|
||||||
// parse: Parser'ın ana giriş noktası. Token listesini alır, AST döndürür.
|
|
||||||
// --------------------------------------------------------------------------
|
|
||||||
inline ASTNode* Parser::parse(TokenList toks) {
|
|
||||||
tokens = toks;
|
|
||||||
current = 0;
|
|
||||||
return parseProgram();
|
|
||||||
}
|
|
||||||
|
|
||||||
// --------------------------------------------------------------------------
|
|
||||||
// parseProgram: Tüm üst seviye deklarasyonları/statement'ları ayrıştırır.
|
|
||||||
//
|
|
||||||
// Program ::= Declaration*
|
|
||||||
// EOF'a (SVR_VOID) kadar parseDeclaration() çağrılır.
|
|
||||||
//
|
|
||||||
// BUG FIX (commit 438bc0e): Eskiden parseExpression() doğrudan çağrılıyordu,
|
|
||||||
// bu sadece tek bir ifadeyi ayrıştırabiliyordu. Şimdi tam program desteği var.
|
|
||||||
// --------------------------------------------------------------------------
|
|
||||||
inline ASTNode* Parser::parseProgram() {
|
|
||||||
ProgramNode* program = new ProgramNode();
|
|
||||||
|
|
||||||
while (currentToken().type != TokenType::SVR_VOID) {
|
|
||||||
ASTNode* decl = parseDeclaration();
|
|
||||||
if (decl)
|
|
||||||
program->addChild(decl);
|
|
||||||
else
|
|
||||||
break; // Hata durumunda döngüden çık
|
|
||||||
}
|
|
||||||
|
|
||||||
return program;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ============================================================================
|
|
||||||
// Deklarasyonlar
|
|
||||||
// ============================================================================
|
|
||||||
|
|
||||||
// --------------------------------------------------------------------------
|
|
||||||
// parseDeclaration: Üst seviye deklarasyon ayrıştırıcı.
|
|
||||||
//
|
|
||||||
// Strateji:
|
|
||||||
// 1. Mevcut token bir tip keyword'ü mü (int, void, float, ...)?
|
|
||||||
// - Evet → lookahead(2) '(' ise → fonksiyon tanımı
|
|
||||||
// - Evet → değilse → değişken tanımı
|
|
||||||
// 2. Değilse → statement (REPL modunda ifade de olabilir)
|
|
||||||
//
|
|
||||||
// LOOKAHEAD KULLANIMI:
|
|
||||||
// "int main()" ve "int x = 10" ayrımı için 2 ileriye bakarız:
|
|
||||||
// - int main() → lookahead(1)=identifier, lookahead(2)='('
|
|
||||||
// - int x = 10 → lookahead(1)=identifier, lookahead(2)='='
|
|
||||||
// --------------------------------------------------------------------------
|
|
||||||
inline ASTNode* Parser::parseDeclaration() {
|
|
||||||
auto ct = currentToken();
|
|
||||||
|
|
||||||
// Tip keyword'ü ile başlayan → fonksiyon veya değişken
|
|
||||||
if (ct.is({
|
|
||||||
TokenType::KW_VOID, TokenType::KW_INT, TokenType::KW_FLOAT_TYPE,
|
|
||||||
TokenType::KW_DOUBLE, TokenType::KW_BOOL, TokenType::KW_CHAR,
|
|
||||||
TokenType::KW_STRING_TYPE, TokenType::KW_AUTO
|
|
||||||
})) {
|
|
||||||
auto la1 = lookahead(1);
|
|
||||||
auto la2 = lookahead(2);
|
|
||||||
// int main( ... ) → fonksiyon
|
|
||||||
if (la1.type == TokenType::IDENTIFIER && la2.type == TokenType::LPAREN)
|
|
||||||
return parseFunctionDecl();
|
|
||||||
// int x ... → değişken
|
|
||||||
return parseVariableDecl();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Tip keyword'ü değil → statement (veya REPL ifadesi)
|
|
||||||
return parseStatement();
|
|
||||||
}
|
|
||||||
|
|
||||||
// --------------------------------------------------------------------------
|
|
||||||
// parseFunctionDecl: Fonksiyon tanımı.
|
|
||||||
//
|
|
||||||
// Sözdizimi: Type Identifier ( [ParamList] ) Block
|
|
||||||
// Örnek: int main() { ... }
|
|
||||||
//
|
|
||||||
// TODO: Parametre listesi ayrıştırma
|
|
||||||
// TODO: Dönüş tipi doğrulama (şu anda string olarak saklanıyor)
|
|
||||||
// --------------------------------------------------------------------------
|
|
||||||
inline ASTNode* Parser::parseFunctionDecl() {
|
|
||||||
FunctionDeclNode* fn = new FunctionDeclNode();
|
|
||||||
fn->returnType = currentToken().token->token; // "int", "void", ...
|
|
||||||
nextToken(); // Dönüş tipini tüket
|
|
||||||
|
|
||||||
fn->name = currentToken().token->token; // "main", "calculate", ...
|
|
||||||
nextToken(); // İsmi tüket
|
|
||||||
|
|
||||||
// Parametre listesi: ( ... )
|
|
||||||
if (currentToken().type == TokenType::LPAREN) {
|
|
||||||
nextToken(); // '(' tüket
|
|
||||||
// TODO: Parametreleri ayrıştır
|
|
||||||
// Şimdilik ')' gelene kadar atla
|
|
||||||
while (currentToken().type != TokenType::RPAREN &&
|
|
||||||
currentToken().type != TokenType::SVR_VOID)
|
|
||||||
nextToken();
|
|
||||||
if (currentToken().type == TokenType::RPAREN)
|
|
||||||
nextToken(); // ')' tüket
|
|
||||||
}
|
|
||||||
|
|
||||||
// Gövde: { ... }
|
|
||||||
if (currentToken().type == TokenType::LBRACE) {
|
|
||||||
ASTNode* body = parseBlock();
|
|
||||||
fn->addChild(body);
|
|
||||||
}
|
|
||||||
|
|
||||||
return fn;
|
|
||||||
}
|
|
||||||
|
|
||||||
// --------------------------------------------------------------------------
|
|
||||||
// parseVariableDecl: Değişken tanımı.
|
|
||||||
//
|
|
||||||
// Sözdizimi: Type Identifier [= Expression] ;
|
|
||||||
// Örnek: int x = 10;
|
|
||||||
// float y; (initExpr = nullptr)
|
|
||||||
//
|
|
||||||
// TODO: Çoklu değişken: int x = 1, y = 2;
|
|
||||||
// --------------------------------------------------------------------------
|
|
||||||
inline ASTNode* Parser::parseVariableDecl() {
|
|
||||||
VariableDeclNode* vd = new VariableDeclNode();
|
|
||||||
vd->varType = currentToken().token->token; // "int", "float", ...
|
|
||||||
nextToken(); // Tipi tüket
|
|
||||||
|
|
||||||
if (currentToken().type != TokenType::IDENTIFIER) {
|
|
||||||
std::cerr << "Parser hatası: değişken ismi bekleniyor\n";
|
|
||||||
return vd; // Hatalı düğüm, çağıran kontrol etmeli
|
|
||||||
}
|
|
||||||
|
|
||||||
vd->name = currentToken().token->token; // "x", "counter", ...
|
|
||||||
nextToken(); // İsmi tüket
|
|
||||||
|
|
||||||
// Opsiyonel başlangıç değeri: = expression
|
|
||||||
if (currentToken().type == TokenType::EQUAL) {
|
|
||||||
nextToken(); // '=' tüket
|
|
||||||
vd->initExpr = parseExpression();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Noktalı virgül (opsiyonel — parser hoşgörülü)
|
|
||||||
if (currentToken().type == TokenType::SEMICOLON)
|
|
||||||
nextToken();
|
|
||||||
|
|
||||||
return vd;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ============================================================================
|
|
||||||
// Statement'lar — Recursive Descent
|
|
||||||
// ============================================================================
|
|
||||||
|
|
||||||
// --------------------------------------------------------------------------
|
|
||||||
// parseStatement: Statement ayrıştırıcı (dispatcher).
|
|
||||||
//
|
|
||||||
// Mevcut token'a göre uygun parse fonksiyonuna yönlendirir.
|
|
||||||
// Sıralama önemli: LBRACE, keyword'ler, değişken tanımı, ifade.
|
|
||||||
// --------------------------------------------------------------------------
|
|
||||||
inline ASTNode* Parser::parseStatement() {
|
|
||||||
auto ct = currentToken();
|
|
||||||
|
|
||||||
if (ct.type == TokenType::LBRACE)
|
|
||||||
return parseBlock();
|
|
||||||
|
|
||||||
if (ct.type == TokenType::KW_IF)
|
|
||||||
return parseIfStatement();
|
|
||||||
|
|
||||||
if (ct.type == TokenType::KW_WHILE)
|
|
||||||
return parseWhileStatement();
|
|
||||||
|
|
||||||
if (ct.type == TokenType::KW_FOR)
|
|
||||||
return parseForStatement();
|
|
||||||
|
|
||||||
if (ct.type == TokenType::KW_DO)
|
|
||||||
return parseDoWhileStatement();
|
|
||||||
|
|
||||||
if (ct.type == TokenType::KW_RETURN)
|
|
||||||
return parseReturnStatement();
|
|
||||||
|
|
||||||
if (ct.type == TokenType::KW_BREAK)
|
|
||||||
return parseBreakStatement();
|
|
||||||
|
|
||||||
if (ct.type == TokenType::KW_CONTINUE)
|
|
||||||
return parseContinueStatement();
|
|
||||||
|
|
||||||
// Değişken tanımı? (tip keyword'ü ile başlayan)
|
|
||||||
if (ct.is({
|
|
||||||
TokenType::KW_VOID, TokenType::KW_INT, TokenType::KW_FLOAT_TYPE,
|
|
||||||
TokenType::KW_DOUBLE, TokenType::KW_BOOL, TokenType::KW_CHAR,
|
|
||||||
TokenType::KW_STRING_TYPE
|
|
||||||
})) {
|
|
||||||
return parseVariableDecl();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Hiçbiri değilse → ifade statement'ı (atama, fonksiyon çağrısı, ...)
|
|
||||||
return parseExpressionStatement();
|
|
||||||
}
|
|
||||||
|
|
||||||
// --------------------------------------------------------------------------
|
|
||||||
// parseBlock: { statement* }
|
|
||||||
// --------------------------------------------------------------------------
|
|
||||||
inline ASTNode* Parser::parseBlock() {
|
|
||||||
BlockNode* block = new BlockNode();
|
|
||||||
|
|
||||||
if (currentToken().type == TokenType::LBRACE)
|
|
||||||
nextToken(); // '{' tüket
|
|
||||||
|
|
||||||
while (currentToken().type != TokenType::RBRACE &&
|
|
||||||
currentToken().type != TokenType::SVR_VOID) {
|
|
||||||
ASTNode* stmt = parseStatement();
|
|
||||||
if (stmt)
|
|
||||||
block->addChild(stmt);
|
|
||||||
else
|
|
||||||
break; // Hata durumunda döngüden çık
|
|
||||||
}
|
|
||||||
|
|
||||||
if (currentToken().type == TokenType::RBRACE)
|
|
||||||
nextToken(); // '}' tüket
|
|
||||||
|
|
||||||
return block;
|
|
||||||
}
|
|
||||||
|
|
||||||
// --------------------------------------------------------------------------
|
|
||||||
// parseIfStatement: if (expression) statement [else statement]
|
|
||||||
//
|
|
||||||
// Süslü parantez zorunlu DEĞİL — tek statement de olabilir.
|
|
||||||
// if (x > 5) return x; ← geçerli
|
|
||||||
// if (x > 5) { ... } ← geçerli
|
|
||||||
//
|
|
||||||
// TODO: Sallantılı else (dangling else) sorunu:
|
|
||||||
// if (a) if (b) x; else y; ← else hangi if'e ait?
|
|
||||||
// Mevcut implementasyon doğru: else en yakın if'e bağlanır.
|
|
||||||
// --------------------------------------------------------------------------
|
|
||||||
inline ASTNode* Parser::parseIfStatement() {
|
|
||||||
IfStatementNode* ifNode = new IfStatementNode();
|
|
||||||
nextToken(); // 'if' tüket
|
|
||||||
|
|
||||||
// Koşul: ( expression )
|
|
||||||
if (currentToken().type == TokenType::LPAREN) {
|
|
||||||
nextToken(); // '(' tüket
|
|
||||||
ifNode->condition = parseExpression();
|
|
||||||
if (currentToken().type == TokenType::RPAREN)
|
|
||||||
nextToken(); // ')' tüket
|
|
||||||
}
|
|
||||||
|
|
||||||
// Then gövdesi
|
|
||||||
ifNode->thenBranch = parseStatement();
|
|
||||||
|
|
||||||
// Opsiyonel else
|
|
||||||
if (currentToken().type == TokenType::KW_ELSE) {
|
|
||||||
nextToken(); // 'else' tüket
|
|
||||||
ifNode->elseBranch = parseStatement();
|
|
||||||
}
|
|
||||||
|
|
||||||
return ifNode;
|
|
||||||
}
|
|
||||||
|
|
||||||
// --------------------------------------------------------------------------
|
|
||||||
// parseWhileStatement: while (expression) statement
|
|
||||||
// --------------------------------------------------------------------------
|
|
||||||
inline ASTNode* Parser::parseWhileStatement() {
|
|
||||||
WhileStatementNode* ws = new WhileStatementNode();
|
|
||||||
nextToken(); // 'while' tüket
|
|
||||||
|
|
||||||
if (currentToken().type == TokenType::LPAREN) {
|
|
||||||
nextToken(); // '(' tüket
|
|
||||||
ws->condition = parseExpression();
|
|
||||||
if (currentToken().type == TokenType::RPAREN)
|
|
||||||
nextToken(); // ')' tüket
|
|
||||||
}
|
|
||||||
|
|
||||||
ws->body = parseStatement();
|
|
||||||
return ws;
|
|
||||||
}
|
|
||||||
|
|
||||||
// --------------------------------------------------------------------------
|
|
||||||
// parseForStatement: for (init; condition; update) statement
|
|
||||||
//
|
|
||||||
// for'un 3 parçası da isteğe bağlıdır:
|
|
||||||
// for (;;) { ... } ← sonsuz döngü (geçerli)
|
|
||||||
//
|
|
||||||
// init: VariableDeclNode veya ExpressionStatementNode
|
|
||||||
// for (int i = 0; ...) → VariableDecl
|
|
||||||
// for (i = 0; ...) → ExpressionStatement
|
|
||||||
// condition: ifade (nullptr = yok)
|
|
||||||
// update: ifade (nullptr = yok)
|
|
||||||
// --------------------------------------------------------------------------
|
|
||||||
inline ASTNode* Parser::parseForStatement() {
|
|
||||||
ForStatementNode* fs = new ForStatementNode();
|
|
||||||
nextToken(); // 'for' tüket
|
|
||||||
|
|
||||||
if (currentToken().type == TokenType::LPAREN)
|
|
||||||
nextToken(); // '(' tüket
|
|
||||||
|
|
||||||
// Init (opsiyonel)
|
|
||||||
if (currentToken().type != TokenType::SEMICOLON)
|
|
||||||
fs->init = parseStatement();
|
|
||||||
if (currentToken().type == TokenType::SEMICOLON)
|
|
||||||
nextToken(); // ';' tüket
|
|
||||||
|
|
||||||
// Condition (opsiyonel)
|
|
||||||
if (currentToken().type != TokenType::SEMICOLON)
|
|
||||||
fs->condition = parseExpression();
|
|
||||||
if (currentToken().type == TokenType::SEMICOLON)
|
|
||||||
nextToken(); // ';' tüket
|
|
||||||
|
|
||||||
// Update (opsiyonel)
|
|
||||||
if (currentToken().type != TokenType::RPAREN)
|
|
||||||
fs->update = parseExpression();
|
|
||||||
if (currentToken().type == TokenType::RPAREN)
|
|
||||||
nextToken(); // ')' tüket
|
|
||||||
|
|
||||||
// Body
|
|
||||||
fs->body = parseStatement();
|
|
||||||
|
|
||||||
return fs;
|
|
||||||
}
|
|
||||||
|
|
||||||
// --------------------------------------------------------------------------
|
|
||||||
// parseDoWhileStatement: do statement while (expression) ;
|
|
||||||
// --------------------------------------------------------------------------
|
|
||||||
inline ASTNode* Parser::parseDoWhileStatement() {
|
|
||||||
DoWhileStatementNode* dw = new DoWhileStatementNode();
|
|
||||||
nextToken(); // 'do' tüket
|
|
||||||
|
|
||||||
// Gövde
|
|
||||||
dw->body = parseStatement();
|
|
||||||
|
|
||||||
// while (expression) ;
|
|
||||||
if (currentToken().type == TokenType::KW_WHILE) {
|
|
||||||
nextToken(); // 'while' tüket
|
|
||||||
if (currentToken().type == TokenType::LPAREN) {
|
|
||||||
nextToken(); // '(' tüket
|
|
||||||
dw->condition = parseExpression();
|
|
||||||
if (currentToken().type == TokenType::RPAREN)
|
|
||||||
nextToken(); // ')' tüket
|
|
||||||
}
|
|
||||||
if (currentToken().type == TokenType::SEMICOLON)
|
|
||||||
nextToken(); // ';' tüket
|
|
||||||
}
|
|
||||||
|
|
||||||
return dw;
|
|
||||||
}
|
|
||||||
|
|
||||||
// --------------------------------------------------------------------------
|
|
||||||
// parseReturnStatement: return [expression] ;
|
|
||||||
//
|
|
||||||
// return; ← value = nullptr (void fonksiyon)
|
|
||||||
// return x + 1; ← value = BinaryExpression
|
|
||||||
// --------------------------------------------------------------------------
|
|
||||||
inline ASTNode* Parser::parseReturnStatement() {
|
|
||||||
ReturnStatementNode* rs = new ReturnStatementNode();
|
|
||||||
nextToken(); // 'return' tüket
|
|
||||||
|
|
||||||
// Opsiyonel dönüş değeri
|
|
||||||
// Eğer sıradaki token ; veya } ise → return;
|
|
||||||
if (currentToken().type != TokenType::SEMICOLON &&
|
|
||||||
currentToken().type != TokenType::RBRACE) {
|
|
||||||
rs->value = parseExpression();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (currentToken().type == TokenType::SEMICOLON)
|
|
||||||
nextToken(); // ';' tüket
|
|
||||||
|
|
||||||
return rs;
|
|
||||||
}
|
|
||||||
|
|
||||||
// --------------------------------------------------------------------------
|
|
||||||
// parseBreakStatement / parseContinueStatement
|
|
||||||
// --------------------------------------------------------------------------
|
|
||||||
inline ASTNode* Parser::parseBreakStatement() {
|
|
||||||
BreakStatementNode* bs = new BreakStatementNode();
|
|
||||||
nextToken(); // 'break' tüket
|
|
||||||
if (currentToken().type == TokenType::SEMICOLON)
|
|
||||||
nextToken();
|
|
||||||
return bs;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline ASTNode* Parser::parseContinueStatement() {
|
|
||||||
ContinueStatementNode* cs = new ContinueStatementNode();
|
|
||||||
nextToken(); // 'continue' tüket
|
|
||||||
if (currentToken().type == TokenType::SEMICOLON)
|
|
||||||
nextToken();
|
|
||||||
return cs;
|
|
||||||
}
|
|
||||||
|
|
||||||
// --------------------------------------------------------------------------
|
|
||||||
// parseExpressionStatement: expression ;
|
|
||||||
//
|
|
||||||
// Bir ifadeyi statement olarak kullanır. Örn: x = 5; foo();
|
|
||||||
//
|
|
||||||
// HATA KURTARMA:
|
|
||||||
// Eğer parseExpression() başarısız olursa (nullptr), sonraki ; veya }
|
|
||||||
// veya EOF'a kadar token'ları atlayarak senkronize olur. Bu, tek bir
|
|
||||||
// hatalı ifadenin tüm parser'ı kilitlemesini önler.
|
|
||||||
//
|
|
||||||
// BUG FIX (commit 438bc0e): Eskiden hatalı ifade durumunda sonsuz
|
|
||||||
// döngüye giriyordu (parseProgram her seferinde aynı ifadeyi okuyordu).
|
|
||||||
// --------------------------------------------------------------------------
|
|
||||||
inline ASTNode* Parser::parseExpressionStatement() {
|
|
||||||
ExpressionStatementNode* es = new ExpressionStatementNode();
|
|
||||||
es->expression = parseExpression();
|
|
||||||
if (!es->expression) {
|
|
||||||
// Hata kurtarma: sonraki güvenli noktaya atla
|
|
||||||
while (currentToken().type != TokenType::SEMICOLON &&
|
|
||||||
currentToken().type != TokenType::RBRACE &&
|
|
||||||
currentToken().type != TokenType::SVR_VOID)
|
|
||||||
nextToken();
|
|
||||||
if (currentToken().type == TokenType::SEMICOLON)
|
|
||||||
nextToken();
|
|
||||||
}
|
|
||||||
if (currentToken().type == TokenType::SEMICOLON)
|
|
||||||
nextToken();
|
|
||||||
|
|
||||||
return es;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ============================================================================
|
|
||||||
// İfadeler — Pratt Parser (Top-Down Operator Precedence)
|
|
||||||
// ============================================================================
|
|
||||||
//
|
|
||||||
// Pratt parser'ın temel fikri: Her operatörün bir "bağlanma gücü" (precedence)
|
|
||||||
// vardır. Parser, bu güce göre operatörleri doğru sırada gruplar.
|
|
||||||
//
|
|
||||||
// NUD (Null Denotation): Prefix ifadeleri (sayılar, -, !, parantez)
|
|
||||||
// LED (Left Denotation): Infix/Postfix ifadeler (+, *, ++)
|
|
||||||
//
|
|
||||||
// ÖRNEK: 1 + 2 * 3
|
|
||||||
// 1. NUD: 1 → Literal(1)
|
|
||||||
// 2. LED(+): prec=13, right'i parseExpression(13) ile ayrıştır
|
|
||||||
// 2a. NUD: 2 → Literal(2)
|
|
||||||
// 2b. LED(*): prec=14 > 13 → parseExpression(14)
|
|
||||||
// 3a. NUD: 3 → Literal(3)
|
|
||||||
// 3b. LED yok → dön
|
|
||||||
// 2c. BinaryExpr(*, 2, 3) dön
|
|
||||||
// 3. BinaryExpr(+, 1, BinaryExpr(*, 2, 3))
|
|
||||||
// Sonuç: 1 + (2 * 3) ✓
|
|
||||||
//
|
|
||||||
// BUG FIX (commit 40579ca): Ana döngü lookahead(1) yerine currentToken()
|
|
||||||
// kullanıyor. NUD artık token'ı tüketip ilerliyor, bu sayede currentToken()
|
|
||||||
// her zaman bir sonraki operatörü gösterir.
|
|
||||||
//
|
|
||||||
// BUG FIX (commit 438bc0e): Atom'lar (sayı, string, identifier) NUD'da
|
|
||||||
// nextToken() ile tüketiliyor. Eskiden tüketilmediği için sonsuz döngü
|
|
||||||
// oluyordu.
|
|
||||||
//
|
|
||||||
// ============================================================================
|
|
||||||
|
|
||||||
// --------------------------------------------------------------------------
|
|
||||||
// parseExpression(): Öncelik 0'dan başla (en düşük bağlanma)
|
|
||||||
// --------------------------------------------------------------------------
|
|
||||||
inline ASTNode* Parser::parseExpression() {
|
|
||||||
return parseExpression(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// --------------------------------------------------------------------------
|
|
||||||
// parseExpression(precedence): Pratt'ın ana döngüsü.
|
|
||||||
//
|
|
||||||
// Algoritma:
|
|
||||||
// 1. NUD ile ilk operand'ı ayrıştır (prefix)
|
|
||||||
// 2. Mevcut token bir operatör mü?
|
|
||||||
// - Evet ve önceliği > precedence ise → LED ile infix ayrıştır
|
|
||||||
// - Hayır veya öncelik <= precedence ise → dur, sol operand'ı döndür
|
|
||||||
// 3. LED'in döndürdüğü düğüm yeni sol operand olur, 2. adıma dön
|
|
||||||
//
|
|
||||||
// DURMA KOŞULLARI:
|
|
||||||
// - RPAREN, SEMICOLON, RBRACE, COMMA: İfade sonu sinyali
|
|
||||||
// - Operatörün önceliği <= mevcut öncelik: Daha sıkı bağlanamaz
|
|
||||||
// --------------------------------------------------------------------------
|
|
||||||
inline ASTNode* Parser::parseExpression(uint16_t precedence) {
|
|
||||||
if (currentToken().type == TokenType::SVR_VOID)
|
|
||||||
return nullptr;
|
|
||||||
|
|
||||||
// 1. Prefix (NUD)
|
|
||||||
ASTNode* left = parseNullDenotation();
|
|
||||||
if (!left) return nullptr;
|
|
||||||
|
|
||||||
// 2. Infix/Postfix döngüsü (LED)
|
|
||||||
while (true) {
|
|
||||||
auto next = currentToken();
|
|
||||||
|
|
||||||
// İfade sonu sinyalleri → dur
|
|
||||||
if (next.type == TokenType::RPAREN ||
|
|
||||||
next.type == TokenType::SEMICOLON ||
|
|
||||||
next.type == TokenType::RBRACE ||
|
|
||||||
next.type == TokenType::COMMA)
|
|
||||||
break;
|
|
||||||
|
|
||||||
// Operatörün bağlanma gücü yetersiz → dur
|
|
||||||
// (daha yüksek öncelikli bir bağlamdayız, bu operatör oraya ait değil)
|
|
||||||
if (precedence < next.getPowerOperator()) {
|
|
||||||
left = parseLeftDenotation(left);
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return left;
|
|
||||||
}
|
|
||||||
|
|
||||||
// --------------------------------------------------------------------------
|
|
||||||
// parseNullDenotation (NUD): Prefix ifadeleri.
|
|
||||||
//
|
|
||||||
// İşlenen prefix tipleri:
|
|
||||||
// - Parantez: ( expression )
|
|
||||||
// - Unary: +expr, -expr, !expr, ~expr, ++expr, --expr
|
|
||||||
// - Literal: 42, "hello", true, false, null
|
|
||||||
// - Identifier: x, myVar
|
|
||||||
//
|
|
||||||
// DÖNÜŞ: Ayrıştırılmış AST düğümü. Token TÜKETİLMİŞ olur (current ilerlemiş).
|
|
||||||
// --------------------------------------------------------------------------
|
|
||||||
inline ASTNode* Parser::parseNullDenotation() {
|
|
||||||
auto ct = currentToken();
|
|
||||||
|
|
||||||
if (ct.type == TokenType::SVR_VOID) {
|
|
||||||
std::cerr << "Parser hatası: beklenmeyen dosya sonu\n";
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- Parantezli ifade: ( expr ) ---
|
|
||||||
// Önceliği sıfırlar — parantez içinde yeni bir ifade başlar.
|
|
||||||
if (ct.type == TokenType::LPAREN) {
|
|
||||||
nextToken(); // '(' tüket
|
|
||||||
ASTNode* expr = parseExpression(0); // Öncelik sıfırla
|
|
||||||
if (currentToken().type == TokenType::RPAREN)
|
|
||||||
nextToken(); // ')' tüket
|
|
||||||
return expr;
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- Unary prefix operatörler: +, -, !, ~, ++, -- ---
|
|
||||||
// PLUS ve MINUS burada UNARY olarak işlenir.
|
|
||||||
// Binary olarak işlenmesi LED tarafından yapılır.
|
|
||||||
//
|
|
||||||
// ÖNEMLİ: PLUS ve MINUS için getPowerOperator() 13 döndürür (binary öncelik).
|
|
||||||
// Ama burada unary olarak kullanılıyor. parseExpression(16) çağırmak daha
|
|
||||||
// doğru olurdu ancak mevcut çalışma şekli de doğru sonuç veriyor.
|
|
||||||
// TODO: Unary için ayrı öncelik seviyesi (örn: 16)
|
|
||||||
if (ct.is({
|
|
||||||
TokenType::PLUS_PLUS, TokenType::MINUS_MINUS,
|
|
||||||
TokenType::PLUS, TokenType::MINUS,
|
|
||||||
TokenType::BANG, TokenType::TILDE
|
|
||||||
})) {
|
|
||||||
nextToken(); // Operatörü tüket
|
|
||||||
// Sağ operand'ı ayrıştır. Unary prefix sağdan sola bağlanır.
|
|
||||||
ASTNode* right = parseExpression(ct.getPowerOperator());
|
|
||||||
BinaryExpressionNode* bin = new BinaryExpressionNode();
|
|
||||||
bin->Right = right;
|
|
||||||
bin->Left = nullptr; // Unary işaretçisi
|
|
||||||
bin->Operator = ct.type;
|
|
||||||
if (right) right->parent = bin;
|
|
||||||
return bin;
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- Sayısal literal: 42, 0xFF, 3.14 ---
|
|
||||||
if (ct.type == TokenType::NUMBER) {
|
|
||||||
nextToken(); // Token'ı tüket
|
|
||||||
LiteralNode* lit = new LiteralNode();
|
|
||||||
lit->lexerToken = ct.token;
|
|
||||||
lit->parserToken = ct;
|
|
||||||
return lit;
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- String literal: "hello" ---
|
|
||||||
if (ct.type == TokenType::STRING) {
|
|
||||||
nextToken();
|
|
||||||
LiteralNode* lit = new LiteralNode();
|
|
||||||
lit->lexerToken = ct.token;
|
|
||||||
lit->parserToken = ct;
|
|
||||||
return lit;
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- Boolean/null literal: true, false, null ---
|
|
||||||
if (ct.is({TokenType::KW_TRUE, TokenType::KW_FALSE, TokenType::KW_NULL})) {
|
|
||||||
nextToken();
|
|
||||||
LiteralNode* lit = new LiteralNode();
|
|
||||||
lit->lexerToken = ct.token;
|
|
||||||
lit->parserToken = ct;
|
|
||||||
return lit;
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- Identifier: x, myVar ---
|
|
||||||
if (ct.type == TokenType::IDENTIFIER) {
|
|
||||||
nextToken();
|
|
||||||
IdentifierNode* id = new IdentifierNode();
|
|
||||||
id->lexerToken = ct.token;
|
|
||||||
id->parserToken = ct;
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
// --------------------------------------------------------------------------
|
|
||||||
// parseLeftDenotation (LED): Infix ve Postfix ifadeler.
|
|
||||||
//
|
|
||||||
// Sol operand zaten ayrıştırılmış olarak gelir (left).
|
|
||||||
// Mevcut token operatördür.
|
|
||||||
//
|
|
||||||
// İşlenen tipler:
|
|
||||||
// - Postfix: expr++, expr--
|
|
||||||
// - Binary infix: expr + expr, expr * expr, expr == expr, ...
|
|
||||||
//
|
|
||||||
// TASARIM NOTU: Postfix ve Binary aynı fonksiyonda işlenir çünkü ikisi de
|
|
||||||
// "sol operand + operatör" pattern'ini takip eder. Postfix'te sağ operand
|
|
||||||
// yoktur.
|
|
||||||
// --------------------------------------------------------------------------
|
|
||||||
inline ASTNode* Parser::parseLeftDenotation(ASTNode* left) {
|
|
||||||
auto ct = currentToken();
|
|
||||||
|
|
||||||
// --- Postfix: expr++, expr-- ---
|
|
||||||
// Operatör operand'dan SONRA gelir, sağ operand yok.
|
|
||||||
if (ct.is({TokenType::PLUS_PLUS, TokenType::MINUS_MINUS})) {
|
|
||||||
nextToken(); // Operatörü tüket
|
|
||||||
PostfixNode* pf = new PostfixNode();
|
|
||||||
pf->operand = left;
|
|
||||||
pf->Operator = ct.type;
|
|
||||||
left->parent = pf;
|
|
||||||
return pf;
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- Binary infix: expr OP expr ---
|
|
||||||
// OP'nin önceliğine göre sağ operand'ı ayrıştır.
|
|
||||||
uint16_t prec = ct.getPowerOperator();
|
|
||||||
nextToken(); // Operatörü tüket
|
|
||||||
|
|
||||||
// Sağ operand. prec parametresi, daha yüksek öncelikli operatörlerin
|
|
||||||
// sağ operand içinde gruplanmasını sağlar.
|
|
||||||
ASTNode* right = parseExpression(prec);
|
|
||||||
|
|
||||||
BinaryExpressionNode* bin = new BinaryExpressionNode();
|
|
||||||
bin->Left = left;
|
|
||||||
bin->Right = right;
|
|
||||||
bin->Operator = ct.type;
|
|
||||||
if (left) left->parent = bin;
|
|
||||||
if (right) right->parent = bin;
|
|
||||||
return bin;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // SAQUT_PARSER
|
#endif // SAQUT_PARSER
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,62 @@
|
||||||
|
// ============================================================================
|
||||||
|
// saQut Compiler — Parser Sınıf Tanımı
|
||||||
|
// ============================================================================
|
||||||
|
//
|
||||||
|
// DİZİN: src/parser/parser_base.hpp
|
||||||
|
// İÇERİK: Parser sınıf tanımı + include'lar. Metot gövdeleri yok.
|
||||||
|
//
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
#ifndef SAQUT_PARSER_BASE
|
||||||
|
#define SAQUT_PARSER_BASE
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <string>
|
||||||
|
#include "parser/token.hpp"
|
||||||
|
#include "parser/ast.hpp"
|
||||||
|
#include "tools.hpp"
|
||||||
|
class Parser {
|
||||||
|
public:
|
||||||
|
ASTNode* parse(TokenList tokens);
|
||||||
|
|
||||||
|
private:
|
||||||
|
TokenList tokens; // Tokenizer'dan gelen token listesi
|
||||||
|
int current = 0; // Şu anki token indeksi
|
||||||
|
|
||||||
|
// --- Token navigasyonu ---
|
||||||
|
ParserToken currentToken();
|
||||||
|
void nextToken();
|
||||||
|
ParserToken lookahead(uint32_t forward);
|
||||||
|
ParserToken parseToken(Token* token);
|
||||||
|
ParserToken getToken(int offset);
|
||||||
|
|
||||||
|
// --- Üst seviye ---
|
||||||
|
ASTNode* parseProgram();
|
||||||
|
|
||||||
|
// --- Deklarasyonlar ---
|
||||||
|
ASTNode* parseDeclaration();
|
||||||
|
ASTNode* parseFunctionDecl();
|
||||||
|
ASTNode* parseStructDecl();
|
||||||
|
ASTNode* parseVariableDecl();
|
||||||
|
|
||||||
|
// --- Statement'lar ---
|
||||||
|
ASTNode* parseStatement();
|
||||||
|
ASTNode* parseBlock();
|
||||||
|
ASTNode* parseIfStatement();
|
||||||
|
ASTNode* parseWhileStatement();
|
||||||
|
ASTNode* parseForStatement();
|
||||||
|
ASTNode* parseDoWhileStatement();
|
||||||
|
ASTNode* parseReturnStatement();
|
||||||
|
ASTNode* parseBreakStatement();
|
||||||
|
ASTNode* parseContinueStatement();
|
||||||
|
ASTNode* parseExpressionStatement();
|
||||||
|
|
||||||
|
// --- İfadeler (Pratt parser) ---
|
||||||
|
ASTNode* parseExpression();
|
||||||
|
ASTNode* parseExpression(uint16_t precedence);
|
||||||
|
ASTNode* parseNullDenotation();
|
||||||
|
ASTNode* parseLeftDenotation(ASTNode* left);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // SAQUT_PARSER_BASE
|
||||||
|
|
@ -1,65 +1,43 @@
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// saQut Compiler — Parser Token Tipleri ve Operatör Öncelik Tablosu
|
// saQut Compiler — Parser Token Tipleri, Operatör Öncelik Tablosu ve ParserToken
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
//
|
//
|
||||||
// DİZİN: src/parser/token.hpp
|
// DİZİN: src/parser/token.hpp
|
||||||
// KATMAN: Katman 3 — Tokenizer ile Parser arasında köprü
|
// KATMAN: Katman 3 — Tokenizer ile Parser Arasında Köprü
|
||||||
// BAĞIMLI: Tokenizer (src/tokenizer/tokenizer.hpp)
|
// AMAÇ: Tokenizer'ın ham token'larını anlamsal tiplere dönüştürmek,
|
||||||
// KULLANAN: AST (src/parser/ast.hpp), Parser (src/parser/parser.hpp)
|
// operatör öncelik ve birleşme kurallarını merkezi olarak tanımlamak
|
||||||
//
|
//
|
||||||
// AMAÇ:
|
// BAĞIMLILIKLAR:
|
||||||
// Tokenizer'ın ürettiği ham Token'ları (string tipli) Parser'ın anlayacağı
|
// - tokenizer/tokenizer.hpp: Token sınıf hiyerarşisi (Token, NumberToken, vs.)
|
||||||
// anlamsal tiplere (TokenType enum) dönüştürür. Ayrıca operatör önceliğini
|
// - KULLANAN: parser/ast.hpp, parser/parser.hpp
|
||||||
// (precedence) ve birleşme yönünü (associativity) merkezi olarak tanımlar.
|
|
||||||
//
|
//
|
||||||
// Bu dosya, Pratt parser'ın "kalbi"dir — tüm operatör önceliği ve birleşme
|
// BU DOSYANIN İÇERDİKLERİ:
|
||||||
// kuralları burada tek bir yerde tanımlanır.
|
// 1. TokenType enum (uint16_t): 100+ token tipi (keyword'ler, operatörler, delimiter'lar)
|
||||||
|
// 2. KEYWORD_MAP: string → TokenType (keyword çözümleme)
|
||||||
|
// 3. OPERATOR_MAP: string → TokenType (operatör çözümleme)
|
||||||
|
// 4. OPERATOR_MAP_REV: TokenType → string (log çıktısı için ters harita)
|
||||||
|
// 5. OPERATOR_MAP_STRREV: TokenType → string (enum ismi, debug için)
|
||||||
|
// 6. TokenPrecedence(): Öncelik tablosu (18 seviye, Pratt parser'ın kalbi)
|
||||||
|
// 7. RightAssociative(): Sağ birleşme kontrolü (atama, üs, ternary)
|
||||||
|
// 8. ParserToken: Parser'ın kullandığı token yapısı (Token* + TokenType)
|
||||||
//
|
//
|
||||||
// ADR-002: Neden Merkezi Operatör Öncelik Tablosu?
|
// TASARIM KARARLARI (ADR-002):
|
||||||
// Recursive descent parser'larda operatör önceliği, her seviye için ayrı
|
// Neden TokenType enum'ı burada tanımlı, Tokenizer'da değil?
|
||||||
// bir fonksiyon yazılarak (parseAddExpr, parseMulExpr, ...) kod tekrarına
|
// -> Tokenizer sadece ham token'lar üretir. Anlamsal tipler Parser'ın işidir.
|
||||||
// neden olur. Yeni bir operatör eklemek için yeni fonksiyon + mevcut
|
// -> Tokenizer'ın Tokenizer'ın ham yapısını değiştirmeden yeni diller eklenebilir.
|
||||||
// fonksiyonları değiştirmek gerekir.
|
|
||||||
//
|
//
|
||||||
// Pratt parser'da tüm öncelik bilgisi TEK BİR TABLODA (TokenPrecedence)
|
// Neden uint16_t tabanlı enum?
|
||||||
// toplanır. Yeni operatör eklemek = tabloya bir satır eklemek.
|
// -> 65K token tipi fazlasıyla yeterli. 2 byte = bellek tasarrufu.
|
||||||
|
// -> Her AST düğümünde TokenType saklanabilir (opsiyonel).
|
||||||
//
|
//
|
||||||
// TASARIM KARARLARI:
|
// Neden dört ayrı map?
|
||||||
// 1. TokenType enum: uint16_t tabanlı. Neden? 65K'dan fazla token tipi
|
// -> unordered_map tek yönlüdür. Her yön için ayrı map gerekir.
|
||||||
// olmayacak, 2 byte yeterli. Bellek tasarrufu AST'de fark eder.
|
// -> OPERATOR_MAP_REV: log çıktısında "+" göstermek için.
|
||||||
|
// -> OPERATOR_MAP_STRREV: enum ismini string olarak (debug, AST dump).
|
||||||
//
|
//
|
||||||
// 2. Üç harita (KEYWORD_MAP, OPERATOR_MAP, OPERATOR_MAP_REV, OPERATOR_MAP_STRREV):
|
// Neden bu kadar çok keyword?
|
||||||
// - KEYWORD_MAP: "if" → KW_IF, string'den TokenType'a
|
// -> saQut hem C/C++ hem Java hem de kendi sözdizimini destekler.
|
||||||
// - OPERATOR_MAP: "+" → PLUS, operatör string'inden TokenType'a
|
// -> Tüm keyword'ler tek enum'da toplanmıştır.
|
||||||
// - OPERATOR_MAP_REV: PLUS → "+", log çıktısı için ters harita
|
|
||||||
// - OPERATOR_MAP_STRREV: PLUS → "PLUS", enum ismini string olarak verir
|
|
||||||
// Neden dört harita? Çünkü std::unordered_map tek yönlüdür.
|
|
||||||
// bidirectional_map kütüphanesi kullanılabilirdi ama bağımlılık istemedik.
|
|
||||||
//
|
|
||||||
// 3. TokenPrecedence(): 18 seviyeli öncelik sistemi.
|
|
||||||
// C/C++/Java standartlarına uygun. Yüksek sayı = yüksek öncelik.
|
|
||||||
// Seviye 18 (en yüksek): üye erişimi (., ->, [], (), ::)
|
|
||||||
// Seviye 1 (en düşük): virgül (,)
|
|
||||||
// Seviye 0: önceliksiz (değerler, EOF, vb.)
|
|
||||||
//
|
|
||||||
// 4. RightAssociative(): Hangi operatörler sağdan sola birleşir?
|
|
||||||
// - Atama (=, +=, vb.)
|
|
||||||
// - Üs alma (**, ^) — matematiksel sağ birleşme: a^b^c = a^(b^c)
|
|
||||||
// - Ternary (?:)
|
|
||||||
// Diğer tüm operatörler soldan sağa birleşir.
|
|
||||||
//
|
|
||||||
// 5. ParserToken yapısı:
|
|
||||||
// Token* token: Tokenizer'ın ürettiği Token'a pointer. Değer kopyası
|
|
||||||
// DEĞİL. Neden pointer? Çünkü Token polimorfik (NumberToken, StringToken,
|
|
||||||
// vb.) ve değer kopyası object slicing'e neden olur.
|
|
||||||
// BUG FIX (commit 40579ca): Eskiden Token token (değer) vardı.
|
|
||||||
// TokenType type: Token'ın anlamsal tipi.
|
|
||||||
// is() / getPowerOperator() / isRightAssociative(): kolaylık metotları.
|
|
||||||
//
|
|
||||||
// BİLİNEN SINIRLAMALAR (TODO):
|
|
||||||
// TODO: Özel operatörler: ?., ??, |>, >>=, vb. (ileride eklenebilir)
|
|
||||||
// TODO: Kullanıcı tanımlı operatör önceliği (DSL'ler için)
|
|
||||||
// TODO: Token konum bilgisi (satır/sütun) ParserToken'a eklenmeli
|
|
||||||
//
|
//
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|
||||||
|
|
@ -107,184 +85,334 @@ typedef std::vector<Token*> TokenList;
|
||||||
// NEDEN uint16_t? Bellek optimizasyonu. Her AST düğümü bir TokenType taşır.
|
// NEDEN uint16_t? Bellek optimizasyonu. Her AST düğümü bir TokenType taşır.
|
||||||
// Binlerce düğümde 2 byte vs 4 byte fark eder.
|
// Binlerce düğümde 2 byte vs 4 byte fark eder.
|
||||||
//
|
//
|
||||||
|
/* ================================================================
|
||||||
|
* TokenType — Anlamsal Token Tipleri
|
||||||
|
* ================================================================
|
||||||
|
*
|
||||||
|
* Tokenizer'ın ürettiği ham token'ları (string tipli) Parser'ın
|
||||||
|
* anlayacağı anlamsal tiplere dönüştürür.
|
||||||
|
*
|
||||||
|
* uint16_t tabanlı — 65K token tipi yeterli.
|
||||||
|
* Bellek: AST düğümlerinde taşınabilir (2 byte).
|
||||||
|
*
|
||||||
|
* KATEGORİLER (öncelik sırasına göre):
|
||||||
|
* 1. Değerler: IDENTIFIER, NUMBER, STRING, SVR_VOID
|
||||||
|
* 2. Keyword'ler: KW_IF ... KW_NOEXCEPT (C/C++/Java ortak)
|
||||||
|
* 3. Operatörler: DOT(18) ... COMMA(1) (Pratt öncelik seviyesi)
|
||||||
|
* 4. Delimiter'lar: LBRACE, RBRACE, SEMICOLON, vb.
|
||||||
|
* 5. Özel: END_OF_FILE, UNKNOWN, COMMENT, PREPROCESSOR
|
||||||
|
*
|
||||||
|
* ================================================================ */
|
||||||
enum class TokenType : uint16_t {
|
enum class TokenType : uint16_t {
|
||||||
// --- Değerler ve Tanımlayıcılar ---
|
/* ====== Değerler ve Tanımlayıcılar ====== */
|
||||||
IDENTIFIER, // değişken/fonksiyon ismi
|
IDENTIFIER, // Değişken/fonksiyon/sınıf ismi.
|
||||||
NUMBER, // 42, 0xFF, 0b1010, 3.14
|
// Tokenizer'da IdentifierToken olarak üretilir.
|
||||||
STRING, // "merhaba"
|
// Örn: x, main, Point, calculateAverage
|
||||||
SVR_VOID, // Geçersiz/EOF sinyali (Parser içinde kullanılır)
|
NUMBER, // Sayısal sabit: 42, 0xFF, 0b1010, 3.14, 1e-5
|
||||||
|
// Tokenizer'da NumberToken olarak üretilir.
|
||||||
|
// .isFloat alanı ile tamsayı/ondalık ayrımı yapılır.
|
||||||
|
STRING, // Metin sabiti: "merhaba", "selam"
|
||||||
|
// Tokenizer'da StringToken olarak üretilir.
|
||||||
|
// Kaçış dizileri (\n, \t, \") tokenizer'da çözülür.
|
||||||
|
SVR_VOID, // Geçersiz/EOF sinyali.
|
||||||
|
// Parser içinde kullanılır. Tokenizer ÜRETMEZ.
|
||||||
|
// currentToken() geçersiz indeks gösterdiğinde döner.
|
||||||
|
|
||||||
// --- Kontrol Akışı Keyword'leri ---
|
/* ====== Kontrol Akışı Keyword'leri ====== */
|
||||||
KW_IF, // if
|
KW_IF, // if (koşullu dal)
|
||||||
KW_ELSE, // else
|
// Sözdizimi: if (koşul) gövde [else gövde]
|
||||||
KW_FOR, // for
|
KW_ELSE, // else (if'in alternatif dalı)
|
||||||
KW_WHILE, // while
|
// Sözdizimi: if (...) ... else ...
|
||||||
KW_DO, // do
|
// Parser'da if'ten sonra else opsiyoneldir.
|
||||||
KW_SWITCH, // switch
|
KW_FOR, // for (tekrarlı döngü)
|
||||||
KW_CASE, // case
|
// Sözdizimi: for (init; koşul; artım) gövde
|
||||||
KW_DEFAULT, // default
|
KW_WHILE, // while (koşullu döngü)
|
||||||
KW_BREAK, // break
|
// Sözdizimi: while (koşul) gövde
|
||||||
KW_CONTINUE, // continue
|
KW_DO, // do (en az bir kez çalışan döngü)
|
||||||
KW_RETURN, // return
|
// Sözdizimi: do gövde while (koşul);
|
||||||
|
KW_SWITCH, // switch (çoklu dal — henüz implemente edilmedi)
|
||||||
|
KW_CASE, // case (switch dalı — henüz implemente edilmedi)
|
||||||
|
KW_DEFAULT, // default (switch varsayılan — henüz implemente edilmedi)
|
||||||
|
KW_BREAK, // break (döngü/switch'ten çık)
|
||||||
|
// Sadece döngü veya switch içinde geçerlidir.
|
||||||
|
KW_CONTINUE, // continue (döngünün bir sonraki iterasyonuna geç)
|
||||||
|
// Sadece döngü içinde geçerlidir.
|
||||||
|
KW_RETURN, // return (fonksiyondan dön)
|
||||||
|
// İsteğe bağlı dönüş değeri: return expr;
|
||||||
|
|
||||||
// --- OOP Keyword'leri ---
|
/* ====== OOP Keyword'leri ====== */
|
||||||
KW_CLASS, // class
|
KW_CLASS, // class (sınıf tanımı — Java/C++ tarzı)
|
||||||
KW_INTERFACE, // interface
|
KW_STRUCT, // struct (yapı tanımı — C tarzı)
|
||||||
KW_ENUM, // enum
|
// saQut'ta class ve struct ikisi de desteklenir.
|
||||||
KW_EXTENDS, // extends
|
KW_INTERFACE, // interface (soyut tip — Java tarzı)
|
||||||
KW_IMPLEMENTS, // implements
|
KW_ENUM, // enum (sabit listesi — C/C++/Java tarzı)
|
||||||
KW_NEW, // new
|
KW_EXTENDS, // extends (kalıtım — Java tarzı)
|
||||||
KW_PUBLIC, // public
|
KW_IMPLEMENTS, // implements (interface gerçekleme — Java tarzı)
|
||||||
KW_PRIVATE, // private
|
KW_NEW, // new (nesne oluşturma — Java/C++ tarzı)
|
||||||
KW_PROTECTED, // protected
|
KW_PUBLIC, // public (erişim belirteci)
|
||||||
KW_STATIC, // static
|
KW_PRIVATE, // private (erişim belirteci)
|
||||||
KW_FINAL, // final
|
KW_PROTECTED, // protected (erişim belirteci — alt sınıflara açık)
|
||||||
KW_ABSTRACT, // abstract
|
KW_STATIC, // static (sınıf üyesi / dosya içi bağlantı)
|
||||||
|
KW_FINAL, // final (değiştirilemez — Java tarzı)
|
||||||
|
KW_ABSTRACT, // abstract (soyut sınıf/metot — Java tarzı)
|
||||||
|
|
||||||
// --- Tip Keyword'leri ---
|
/* ====== Tip Keyword'leri ====== */
|
||||||
KW_VOID, // void
|
KW_VOID, // void (değer döndürmeyen fonksiyon / tip yok)
|
||||||
KW_BOOL, // bool
|
// C/C++/Java uyumluluğu için.
|
||||||
KW_INT, // int
|
KW_BOOL, // bool (mantıksal tip: true/false)
|
||||||
KW_FLOAT_TYPE, // float (FLOAT math.h'de tanımlı olabilir, TYPE eki var)
|
// C++ bool ile aynı.
|
||||||
KW_DOUBLE, // double
|
KW_INT, // int (tamsayı tipi: 32-bit işaretli)
|
||||||
KW_CHAR, // char
|
// Varsayılan tamsayı tipi.
|
||||||
KW_STRING_TYPE, // string
|
KW_FLOAT_TYPE, // float (32-bit ondalıklı sayı)
|
||||||
|
// FLOAT_MATH hatasından kaçınmak için _TYPE eki.
|
||||||
|
// math.h'deki float tanımıyla çakışmaz.
|
||||||
|
KW_DOUBLE, // double (64-bit ondalıklı sayı)
|
||||||
|
KW_CHAR, // char (8-bit karakter)
|
||||||
|
// Tek tırnak içindeki karakterler için: 'A'
|
||||||
|
KW_STRING_TYPE, // string (metin tipi)
|
||||||
|
// string.h'daki string işlevleriyle çakışmaz.
|
||||||
|
|
||||||
// --- Literal Keyword'ler ---
|
/* ====== Literal Keyword'ler ====== */
|
||||||
KW_TRUE, // true
|
KW_TRUE, // true (mantıksal doğru sabiti)
|
||||||
KW_FALSE, // false
|
// Boolean literal: if (true) { ... }
|
||||||
KW_NULL, // null
|
KW_FALSE, // false (mantıksal yanlış sabiti)
|
||||||
|
// Boolean literal: while (false) { ... }
|
||||||
|
KW_NULL, // null (boş referans sabiti)
|
||||||
|
// Pointer/referans tipleri için: Object obj = null;
|
||||||
|
|
||||||
// --- İstisna Yönetimi ---
|
/* ====== İstisna Yönetimi ====== */
|
||||||
KW_TRY, // try
|
KW_TRY, // try (istisna deneme bloğu — Java/C++ tarzı)
|
||||||
KW_CATCH, // catch
|
// Sözdizimi: try { ... } catch (Ex e) { ... }
|
||||||
KW_FINALLY, // finally
|
KW_CATCH, // catch (istisna yakalama bloğu)
|
||||||
KW_THROW, // throw
|
KW_FINALLY, // finally (her durumda çalışan blok — Java tarzı)
|
||||||
KW_THROWS, // throws
|
KW_THROW, // throw (istisna fırlatma — C++/Java tarzı)
|
||||||
KW_ASSERT, // assert
|
// Sözdizimi: throw new Exception("hata");
|
||||||
|
KW_THROWS, // throws (metot imzasında istisna bildirimi — Java)
|
||||||
|
KW_ASSERT, // assert (debug assertions — C/Java tarzı)
|
||||||
|
|
||||||
// --- Modül/Paket ---
|
/* ====== Modül/Paket ====== */
|
||||||
KW_IMPORT, // import
|
KW_IMPORT, // import (modül içe aktarma — Java/Python tarzı)
|
||||||
KW_PACKAGE, // package
|
// Sözdizimi: import java.util.List;
|
||||||
|
KW_PACKAGE, // package (modül bildirimi — Java tarzı)
|
||||||
|
// Sözdizimi: package com.saqut.compiler;
|
||||||
|
|
||||||
// --- C/C++ Ekleri ---
|
/* ====== C/C++ Ekleri ====== */
|
||||||
KW_NATIVE, // native (JNI)
|
KW_NATIVE, // native (yerel kod bildirimi — JNI)
|
||||||
KW_SYNCHRONIZED, // synchronized (Java)
|
// Java native metotları için.
|
||||||
KW_VOLATILE, // volatile
|
KW_SYNCHRONIZED, // synchronized (iş parçacığı senkronizasyonu — Java)
|
||||||
KW_TRANSIENT, // transient
|
KW_VOLATILE, // volatile (derleyici optimizasyonunu engelle — C/C++/Java)
|
||||||
KW_CONST, // const
|
KW_TRANSIENT, // transient (serileştirmeyi atla — Java)
|
||||||
KW_EXTERN, // extern
|
KW_CONST, // const (değişmez değer — C/C++ tarzı)
|
||||||
KW_TYPEDEF, // typedef
|
// Örn: const int MAX = 100;
|
||||||
KW_SIZEOF, // sizeof
|
KW_EXTERN, // extern (harici bağlantı — C/C++ tarzı)
|
||||||
KW_ALIGNOF, // alignof
|
KW_TYPEDEF, // typedef (tip takma adı — C/C++ tarzı)
|
||||||
KW_DECLTYPE, // decltype
|
KW_SIZEOF, // sizeof (tip/boyut sorgulama — C/C++ tarzı)
|
||||||
KW_AUTO, // auto
|
// Sözdizimi: sizeof(int) veya sizeof x
|
||||||
KW_CONSTEXPR, // constexpr
|
KW_ALIGNOF, // alignof (hizalama sorgulama — C++11)
|
||||||
KW_NOEXCEPT, // noexcept
|
KW_DECLTYPE, // decltype (ifade tipi çıkarımı — C++11)
|
||||||
|
KW_AUTO, // auto (otomatik tip çıkarımı — C++11)
|
||||||
|
KW_CONSTEXPR, // constexpr (derleme zamanı sabiti — C++11)
|
||||||
|
KW_NOEXCEPT, // noexcept (istisna fırlatmayan bildirimi — C++11)
|
||||||
|
|
||||||
// ================================================================
|
/* ================================================================
|
||||||
// Operatörler — Öncelik sırasına göre gruplanmış
|
* Operatörler — Öncelik sırasına göre gruplanmış
|
||||||
// ================================================================
|
*
|
||||||
|
* Her operatörün yanında Pratt parser öncelik seviyesi yazılıdır.
|
||||||
|
* Yüksek sayı = daha sıkı bağlanma (önce işlenir).
|
||||||
|
*
|
||||||
|
* Seviye 18 (en yüksek): Üye erişimi ve çağrı
|
||||||
|
* Seviye 17: Postfix ++ --
|
||||||
|
* Seviye 16: Unary prefix + - ! ~
|
||||||
|
* Seviye 15: Üs alma ** ^
|
||||||
|
* Seviye 14: Çarpma/Bölme * / %
|
||||||
|
* Seviye 13: Toplama/Çıkarma + -
|
||||||
|
* Seviye 12: Bitsel kaydırma << >>
|
||||||
|
* Seviye 11: İlişkisel < <= > >=
|
||||||
|
* Seviye 10: Eşitlik == !=
|
||||||
|
* Seviye 9: Bitsel VE &
|
||||||
|
* Seviye 8: Bitsel XOR ^ (CARET üs olarak 15'te)
|
||||||
|
* Seviye 7: Bitsel VEYA |
|
||||||
|
* Seviye 6: Mantıksal VE &&
|
||||||
|
* Seviye 5: Mantıksal VEYA ||
|
||||||
|
* Seviye 4: Ternary ?
|
||||||
|
* Seviye 3: Ternary else :
|
||||||
|
* Seviye 2: Atama = += -= vb.
|
||||||
|
* Seviye 1 (en düşük): Virgül ,
|
||||||
|
* ================================================================ */
|
||||||
|
|
||||||
// Seviye 1 (18): Üye erişimi ve çağrı — En yüksek öncelik
|
// Seviye 18: Üye erişimi ve çağrı — En yüksek öncelik
|
||||||
DOT, // .
|
DOT, // . (üye erişimi) — obj.field
|
||||||
ARROW, // ->
|
// Öncelik 18. En sıkı bağlanan operatör.
|
||||||
LBRACKET, // [
|
ARROW, // -> (pointer üye erişimi) — ptr->field
|
||||||
RBRACKET, // ]
|
// C++ tarzı. Öncelik 18.
|
||||||
LPAREN, // (
|
LBRACKET, // [ (dizi/indeks erişimi başlangıcı) — a[i]
|
||||||
RPAREN, // )
|
// Açılış köşeli parantez. Öncelik 18.
|
||||||
|
RBRACKET, // ] (dizi/indeks erişimi bitişi) — a[i]
|
||||||
|
// Kapanış köşeli parantez. Tek başına kullanılmaz.
|
||||||
|
LPAREN, // ( (fonksiyon çağrısı/grouping başlangıcı)
|
||||||
|
// İki anlamı: f(args) çağrı, (expr) gruplama.
|
||||||
|
// Öncelik 18.
|
||||||
|
RPAREN, // ) (fonksiyon çağrısı/grouping bitişi)
|
||||||
|
// Kapanış parantez. Tek başına kullanılmaz.
|
||||||
|
|
||||||
// Seviye 2 (17): Postfix
|
// Seviye 17: Postfix — Soldaki ifadeye sonradan uygulanan operatörler
|
||||||
PLUS_PLUS, // ++ (postfix)
|
PLUS_PLUS, // ++ (postfix artım) — x++
|
||||||
MINUS_MINUS, // -- (postfix)
|
// Önce x'in değerini döndür, sonra artır.
|
||||||
|
// Öncelik 17. Sağ birleşmeli DEĞİL.
|
||||||
|
MINUS_MINUS, // -- (postfix azaltım) — x--
|
||||||
|
// Önce x'in değerini döndür, sonra azalt.
|
||||||
|
// Öncelik 17.
|
||||||
|
|
||||||
// Seviye 3 (16): Unary Prefix
|
// Seviye 16: Unary Prefix — Sağındaki ifadeye uygulanan tekli operatörler
|
||||||
PLUS, // + (unary)
|
PLUS, // + (unary plus / binary toplama)
|
||||||
MINUS, // - (unary)
|
// Unary: +x (pozitif işareti, genelde etkisiz).
|
||||||
BANG, // ! (mantıksal değil)
|
// Binary: a + b (toplama, öncelik 13).
|
||||||
TILDE, // ~ (bitsel değil)
|
// Hangi anlamda kullanıldığı parse bağlamında belirlenir.
|
||||||
|
MINUS, // - (unary minus / binary çıkarma)
|
||||||
|
// Unary: -x (negatif yap).
|
||||||
|
// Binary: a - b (çıkarma, öncelik 13).
|
||||||
|
BANG, // ! (mantıksal değil) — !x
|
||||||
|
// Örn: if (!flag) { ... }
|
||||||
|
// Sadece prefix. Öncelik 16.
|
||||||
|
TILDE, // ~ (bitsel değil) — ~x
|
||||||
|
// Bitwise NOT. Sadece prefix. Öncelik 16.
|
||||||
|
|
||||||
// Seviye 4 (15): Üs alma
|
// Seviye 15: Üs alma — Sağ birleşmeli
|
||||||
STAR_STAR, // ** (Python tarzı üs)
|
STAR_STAR, // ** (üs alma) — a ** b = a^b
|
||||||
CARET, // ^ (bazı dillerde üs)
|
// Python tarzı. Öncelik 15. Sağ birleşmeli.
|
||||||
|
// 2 ** 3 ** 2 = 2 ** (3 ** 2) = 512
|
||||||
|
CARET, // ^ (üs alma veya bitsel XOR)
|
||||||
|
// saQut'ta varsayılan: üs alma (öncelik 15).
|
||||||
|
// C/C++'da XOR (öncelik 8) — bağlama göre değişebilir.
|
||||||
|
|
||||||
// Seviye 5 (14): Çarpma/Bölme
|
// Seviye 14: Çarpma/Bölme — Sol birleşmeli
|
||||||
STAR, // *
|
STAR, // * (çarpma) — a * b
|
||||||
SLASH, // /
|
// Öncelik 14.
|
||||||
PERCENT, // %
|
SLASH, // / (bölme) — a / b
|
||||||
|
// Tamsayı bölmesi: int / int = int.
|
||||||
|
// Ondalık bölme: float / float = float.
|
||||||
|
PERCENT, // % (mod alma) — a % b
|
||||||
|
// Sadece tamsayılar için.
|
||||||
|
|
||||||
// Seviye 6 (13): Toplama/Çıkarma — PLUS ve MINUS yukarıda (unary + binary)
|
// Seviye 13: Toplama/Çıkarma
|
||||||
// Seviye 7 (12): Bitsel kaydırma
|
// PLUS ve MINUS yukarıda tanımlandı (hem unary 16 hem binary 13).
|
||||||
LSHIFT, // <<
|
// Pratt parser bağlama göre doğru önceliği kullanır.
|
||||||
RSHIFT, // >>
|
|
||||||
|
|
||||||
// Seviye 8 (11): İlişkisel karşılaştırma
|
// Seviye 12: Bitsel kaydırma
|
||||||
LESS, // <
|
LSHIFT, // << (sola kaydırma) — a << b
|
||||||
LESS_EQUAL, // <=
|
// a * 2^b. Öncelik 12.
|
||||||
GREATER, // >
|
RSHIFT, // >> (sağa kaydırma) — a >> b
|
||||||
GREATER_EQUAL, // >=
|
// a / 2^b (işaretli: arithmetic, işaretsiz: logical).
|
||||||
|
|
||||||
// Seviye 9 (10): Eşitlik
|
// Seviye 11: İlişkisel karşılaştırma
|
||||||
EQUAL_EQUAL, // ==
|
LESS, // < (küçüktür) — a < b
|
||||||
BANG_EQUAL, // !=
|
// true/false döndürür.
|
||||||
|
LESS_EQUAL, // <= (küçük eşittir) — a <= b
|
||||||
|
GREATER, // > (büyüktür) — a > b
|
||||||
|
GREATER_EQUAL, // >= (büyük eşittir) — a >= b
|
||||||
|
|
||||||
// Seviye 10 (9): Bitsel VE
|
// Seviye 10: Eşitlik
|
||||||
AMPERSAND, // &
|
EQUAL_EQUAL, // == (eşittir) — a == b
|
||||||
|
// Değer eşitliği. Öncelik 10.
|
||||||
|
BANG_EQUAL, // != (eşit değildir) — a != b
|
||||||
|
|
||||||
// Seviye 11 (8): Bitsel XOR — CARET yukarıda (üs veya XOR)
|
// Seviye 9: Bitsel VE
|
||||||
|
AMPERSAND, // & (bitsel VE) — a & b
|
||||||
|
// Bitwise AND. Öncelik 9.
|
||||||
|
|
||||||
// Seviye 12 (7): Bitsel VEYA
|
// Seviye 8: Bitsel XOR
|
||||||
PIPE, // |
|
// ^ (CARET) yukarıda (üs olarak seviye 15'te).
|
||||||
|
// Gelecekte XOR için ayrı token eklenebilir.
|
||||||
|
|
||||||
// Seviye 13 (6): Mantıksal VE
|
// Seviye 7: Bitsel VEYA
|
||||||
AMPERSAND_AMPERSAND, // &&
|
PIPE, // | (bitsel VEYA) — a | b
|
||||||
|
// Bitwise OR. Öncelik 7.
|
||||||
|
|
||||||
// Seviye 14 (5): Mantıksal VEYA
|
// Seviye 6: Mantıksal VE
|
||||||
PIPE_PIPE, // ||
|
AMPERSAND_AMPERSAND, // && (mantıksal VE) — a && b
|
||||||
|
// Kısa devre (short-circuit): a false ise b değerlendirilmez.
|
||||||
|
// Öncelik 6.
|
||||||
|
|
||||||
// Seviye 15 (4): Üçlü koşul (ternary)
|
// Seviye 5: Mantıksal VEYA
|
||||||
TERNARY, // ?
|
PIPE_PIPE, // || (mantıksal VEYA) — a || b
|
||||||
COLON, // : (ternary ve etiket için)
|
// Kısa devre: a true ise b değerlendirilmez.
|
||||||
|
// Öncelik 5.
|
||||||
|
|
||||||
// Seviye 16 (3): Atama
|
// Seviye 4: Üçlü koşul (ternary) — Sağ birleşmeli
|
||||||
EQUAL, // =
|
TERNARY, // ? (ternary if) — a ? b : c
|
||||||
PLUS_EQUAL, // +=
|
// Öncelik 4. Sağ birleşmeli.
|
||||||
MINUS_EQUAL, // -=
|
// a ? b : c ? d : e = a ? b : (c ? d : e)
|
||||||
STAR_EQUAL, // *=
|
COLON, // : (ternary else / etiket) — ternary'in ikinci kısmı
|
||||||
SLASH_EQUAL, // /=
|
// Ternary'de öncelik 3 (0 değil!).
|
||||||
PERCENT_EQUAL, // %=
|
// Ayrıca switch/case etiketleri için de kullanılır.
|
||||||
AMPERSAND_EQUAL, // &=
|
|
||||||
PIPE_EQUAL, // |=
|
|
||||||
CARET_EQUAL, // ^=
|
|
||||||
LSHIFT_EQUAL, // <<=
|
|
||||||
RSHIFT_EQUAL, // >>=
|
|
||||||
|
|
||||||
// --- Diğer Semboller ---
|
// Seviye 2: Atama — Sağ birleşmeli
|
||||||
LBRACE, // {
|
EQUAL, // = (basit atama) — a = b
|
||||||
RBRACE, // }
|
// Öncelik 2. Sağ birleşmeli.
|
||||||
SEMICOLON, // ;
|
// a = b = 5 = a = (b = 5)
|
||||||
COMMA, // ,
|
PLUS_EQUAL, // += (topla ve ata) — a += b → a = a + b
|
||||||
COLON_COLON, // ::
|
MINUS_EQUAL, // -= (çıkar ve ata) — a -= b → a = a - b
|
||||||
|
STAR_EQUAL, // *= (çarp ve ata) — a *= b → a = a * b
|
||||||
|
SLASH_EQUAL, // /= (böl ve ata) — a /= b → a = a / b
|
||||||
|
PERCENT_EQUAL, // %= (mod al ve ata) — a %= b → a = a % b
|
||||||
|
AMPERSAND_EQUAL, // &= (bitsel VE ve ata) — a &= b → a = a & b
|
||||||
|
PIPE_EQUAL, // |= (bitsel VEYA ve ata) — a |= b → a = a | b
|
||||||
|
CARET_EQUAL, // ^= (XOR ve ata) — a ^= b → a = a ^ b
|
||||||
|
LSHIFT_EQUAL, // <<= (sola kaydır ve ata) — a <<= b → a = a << b
|
||||||
|
RSHIFT_EQUAL, // >>= (sağa kaydır ve ata) — a >>= b → a = a >> b
|
||||||
|
|
||||||
// --- Özel ---
|
/* ====== Diğer Semboller ====== */
|
||||||
END_OF_FILE, // Dosya sonu
|
LBRACE, // { (açılış süslü parantez) — blok başlangıcı
|
||||||
UNKNOWN, // Bilinmeyen karakter
|
// Sözdizimi: { statement1; statement2; }
|
||||||
COMMENT, // Yorum (// veya /* */) — şu anda token üretilmez
|
RBRACE, // } (kapanış süslü parantez) — blok bitişi
|
||||||
PREPROCESSOR, // Önişlemci (#) — şu anda kullanılmıyor
|
SEMICOLON, // ; (noktalı virgül) — ifade sonu belirteci
|
||||||
|
// C/C++/Java tarzında her ifade ; ile biter.
|
||||||
|
COMMA, // , (virgül) — ifade ayırıcı
|
||||||
|
// Örn: int a, b, c; veya f(1, 2, 3)
|
||||||
|
// Öncelik 1 (en düşük).
|
||||||
|
COLON_COLON, // :: (kapsam çözümleme) — Class::method
|
||||||
|
// C++ tarzı. Şu anda sadece token tanımlı, parse yok.
|
||||||
|
|
||||||
|
/* ====== Özel Token'lar ====== */
|
||||||
|
END_OF_FILE, // Dosya sonu belirteci.
|
||||||
|
// Tokenizer dosya sonuna gelindiğinde üretir.
|
||||||
|
// Parser'ın durma koşuludur.
|
||||||
|
UNKNOWN, // Bilinmeyen/tanınamayan karakter.
|
||||||
|
// Tokenizer'ın çözemediği her şey.
|
||||||
|
// Hata raporlamada kullanılır.
|
||||||
|
COMMENT, // Yorum token'ı (// veya /* */).
|
||||||
|
// ŞU ANDA TOKEN ÜRETİLMEZ — tokenizer yorumları atlar.
|
||||||
|
// Gelecekte belge yorumları (///, /** */) için kullanılabilir.
|
||||||
|
PREPROCESSOR, // Önişlemci direktifi (#).
|
||||||
|
// ŞU ANDA KULLANILMIYOR — C önişlemcisi yok.
|
||||||
|
// Gelecekte #include, #define için.
|
||||||
};
|
};
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// KEYWORD_MAP — Keyword String → TokenType
|
// KEYWORD_MAP — Keyword String → TokenType Dönüşüm Haritası
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
//
|
//
|
||||||
// Tokenizer'ın ürettiği KeywordToken'ların token değerini (örn: "if")
|
// AMAÇ: Tokenizer'ın ürettiği KeywordToken'ların token değerini (örn: "if")
|
||||||
// Parser'ın anlayacağı TokenType'a (KW_IF) dönüştürür.
|
// Parser'ın anlayacağı TokenType'a (KW_IF) dönüştürür.
|
||||||
//
|
//
|
||||||
// std::unordered_map: O(1) ortalama arama. const: derleme zamanı sabiti.
|
// ANAHTAR: std::string_view — keyword string'i (kopyalanmaz, salt okunur)
|
||||||
// std::string_view: string kopyalamadan kaçınır.
|
// DEĞER: TokenType — Parser'ın anlayacağı anlamsal tip
|
||||||
//
|
//
|
||||||
// NOT: Bu harita, Tokenizer'daki keywords[] dizisi ile eşleşmelidir.
|
// VERİ YAPISI: std::unordered_map<string_view, TokenType>
|
||||||
// Birinde ekleme yapılırsa diğerine de eklenmelidir.
|
// - O(1) ortalama arama süresi
|
||||||
|
// - constexpr: derleme zamanı sabiti (derleyici tabloya gömer)
|
||||||
|
// - std::string_view: string kopyalamadan kaçınır (performans)
|
||||||
|
//
|
||||||
|
// BOYUT: ~60 girdi (tüm keyword'ler)
|
||||||
|
// NEDEN unordered_map, neden map değil?
|
||||||
|
// - Arama sıklığı: her token için bir kez
|
||||||
|
// - unordered_map O(1) vs map O(log n) — fark küçük ama var
|
||||||
|
// - Sıralı erişim gerekmez
|
||||||
|
//
|
||||||
|
// SENKRONİZASYON UYARISI:
|
||||||
|
// Bu harita, Tokenizer'daki keywords[] dizisi İLE EŞLEŞMELİDİR.
|
||||||
|
// Birinde ekleme yapılırsa diğerine de eklenmelidir.
|
||||||
|
// TODO: İki listeyi ortak bir kaynaktan üretecek bir makro/kod üreteci.
|
||||||
//
|
//
|
||||||
inline const std::unordered_map<std::string_view, TokenType> KEYWORD_MAP = {
|
inline const std::unordered_map<std::string_view, TokenType> KEYWORD_MAP = {
|
||||||
// --- Control flow ---
|
// --- Control flow ---
|
||||||
|
|
@ -302,6 +430,7 @@ inline const std::unordered_map<std::string_view, TokenType> KEYWORD_MAP = {
|
||||||
|
|
||||||
// --- OOP ---
|
// --- OOP ---
|
||||||
{"class", TokenType::KW_CLASS},
|
{"class", TokenType::KW_CLASS},
|
||||||
|
{"struct", TokenType::KW_STRUCT},
|
||||||
{"interface", TokenType::KW_INTERFACE},
|
{"interface", TokenType::KW_INTERFACE},
|
||||||
{"enum", TokenType::KW_ENUM},
|
{"enum", TokenType::KW_ENUM},
|
||||||
{"extends", TokenType::KW_EXTENDS},
|
{"extends", TokenType::KW_EXTENDS},
|
||||||
|
|
@ -357,16 +486,30 @@ inline const std::unordered_map<std::string_view, TokenType> KEYWORD_MAP = {
|
||||||
};
|
};
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// OPERATOR_MAP — Operatör/Delimiter String → TokenType
|
// OPERATOR_MAP — Operatör/Delimiter String → TokenType Dönüşüm Haritası
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
//
|
//
|
||||||
// Tokenizer'ın ürettiği OperatorToken ve DelimiterToken'ları TokenType'a
|
// AMAÇ: Tokenizer'ın ürettiği OperatorToken ve DelimiterToken'ları TokenType'a
|
||||||
// dönüştürür. Her iki token tipi de aynı haritayı kullanır çünkü parser
|
// dönüştürür. Her iki token tipi de aynı haritayı kullanır çünkü
|
||||||
// seviyesinde delimiter'lar da operatör gibi işlenir.
|
// Parser seviyesinde delimiter'lar da operatör gibi işlenir.
|
||||||
//
|
//
|
||||||
// SIRALAMA ÖNEMLİ DEĞİL (unordered_map).
|
// ANAHTAR: std::string_view — operatör/delimiter string'i (örn: "+", "->", "{")
|
||||||
// Ama Tokenizer'daki operators[] ve delimiters[] dizilerindeki sıralama
|
// DEĞER: TokenType — Parser'ın anlayacağı anlamsal tip
|
||||||
// önemlidir — çok karakterliler önce gelmelidir.
|
//
|
||||||
|
// VERİ YAPISI: std::unordered_map<string_view, TokenType>
|
||||||
|
// - O(1) ortalama arama
|
||||||
|
// - const: derleme zamanı sabiti
|
||||||
|
// - Boyut: ~40 girdi
|
||||||
|
//
|
||||||
|
// NEDEN İKİ AYRI HARİTA DEĞİL (operator + delimiter)?
|
||||||
|
// - Parser seviyesinde fark yok: {, }, ; hepsi operatör gibi işlenir.
|
||||||
|
// - Tek harita = tek arama = daha basit kod.
|
||||||
|
//
|
||||||
|
// SIRALAMA UYARISI:
|
||||||
|
// Bu haritada sıralama önemli DEĞİL (unordered_map).
|
||||||
|
// ANCAK Tokenizer'daki operators[] ve delimiters[] dizilerindeki
|
||||||
|
// sıralama ÖNEMLİDİR — çok karakterliler önce gelmelidir!
|
||||||
|
// Örn: "->" önce, "-" sonra kontrol edilmelidir.
|
||||||
//
|
//
|
||||||
inline const std::unordered_map<std::string_view, TokenType> OPERATOR_MAP = {
|
inline const std::unordered_map<std::string_view, TokenType> OPERATOR_MAP = {
|
||||||
// --- 2 karakterli ---
|
// --- 2 karakterli ---
|
||||||
|
|
@ -426,12 +569,21 @@ inline const std::unordered_map<std::string_view, TokenType> OPERATOR_MAP = {
|
||||||
};
|
};
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// OPERATOR_MAP_REV — TokenType → Operatör String (Log için)
|
// OPERATOR_MAP_REV — TokenType → Operatör String (Log/Görüntüleme İçin)
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
//
|
//
|
||||||
// AST ağacını konsola yazdırırken (log) TokenType enum değerini insan
|
// AMAÇ: TokenType enum değerini insan tarafından okunabilir operatör
|
||||||
// tarafından okunabilir operatör sembolüne dönüştürür.
|
// sembolüne dönüştürür. Log çıktısı ve hata mesajları için kullanılır.
|
||||||
// Örn: TokenType::PLUS → "+"
|
//
|
||||||
|
// ANAHTAR: TokenType — enum değeri (örn: TokenType::PLUS)
|
||||||
|
// DEĞER: std::string_view — operatör sembolü (örn: "+")
|
||||||
|
//
|
||||||
|
// KULLANIM:
|
||||||
|
// auto it = OPERATOR_MAP_REV.find(type);
|
||||||
|
// if (it != OPERATOR_MAP_REV.end()) std::cout << it->second;
|
||||||
|
//
|
||||||
|
// NOT: TokenType::IDENTIFIER, NUMBER, STRING, KW_* ve özel token'lar
|
||||||
|
// bu haritada YOKTUR (operatör değiller). Onlar için ayrı dönüşüm gerekir.
|
||||||
//
|
//
|
||||||
inline const std::unordered_map<TokenType, std::string_view> OPERATOR_MAP_REV = {
|
inline const std::unordered_map<TokenType, std::string_view> OPERATOR_MAP_REV = {
|
||||||
{TokenType::ARROW, "->"},
|
{TokenType::ARROW, "->"},
|
||||||
|
|
@ -484,11 +636,24 @@ inline const std::unordered_map<TokenType, std::string_view> OPERATOR_MAP_REV =
|
||||||
};
|
};
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// OPERATOR_MAP_STRREV — TokenType → Enum İsmi (Log için)
|
// OPERATOR_MAP_STRREV — TokenType → Enum İsmi (Debug/Log İçin)
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
//
|
//
|
||||||
// AST log çıktısında operatörün enum ismini gösterir.
|
// AMAÇ: AST log çıktısında operatörün enum ismini (string olarak) gösterir.
|
||||||
// Örn: TokenType::PLUS → "PLUS"
|
// OPERATOR_MAP_REV'den farkı: sembol yerine enum adı döndürür.
|
||||||
|
//
|
||||||
|
// KULLANIM:
|
||||||
|
// AST dump/debug çıktısı: TokenPrecedence(PLUS) yerine "PLUS(13)" gösterimi.
|
||||||
|
//
|
||||||
|
// ANAHTAR: TokenType — enum değeri (örn: TokenType::PLUS)
|
||||||
|
// DEĞER: std::string_view — enum ismi (örn: "PLUS")
|
||||||
|
//
|
||||||
|
// ÖRN: TokenType::PLUS → "PLUS"
|
||||||
|
// TokenType::PLUS_EQUAL → "PLUS_EQUAL"
|
||||||
|
//
|
||||||
|
// NOT: İki harita da (REV ve STRREV) aynı anahtarları içerir ama farklı değerler.
|
||||||
|
// REV: "+" (operatör sembolü)
|
||||||
|
// STRREV: "PLUS" (enum ismi)
|
||||||
//
|
//
|
||||||
inline const std::unordered_map<TokenType, std::string_view> OPERATOR_MAP_STRREV = {
|
inline const std::unordered_map<TokenType, std::string_view> OPERATOR_MAP_STRREV = {
|
||||||
{TokenType::ARROW, "ARROW"},
|
{TokenType::ARROW, "ARROW"},
|
||||||
|
|
@ -541,40 +706,50 @@ inline const std::unordered_map<TokenType, std::string_view> OPERATOR_MAP_STRREV
|
||||||
};
|
};
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// TokenPrecedence — Operatör Öncelik Tablosu
|
// TokenPrecedence — Operatör Öncelik Tablosu (Pratt Parser'ın Kalbi)
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
//
|
//
|
||||||
// Pratt parser'ın kalbi. Her TokenType için bir öncelik seviyesi döndürür.
|
// AMAÇ: Her TokenType için bir öncelik seviyesi döndürür.
|
||||||
// Yüksek sayı = daha sıkı bağlanma (daha yüksek öncelik).
|
// Yüksek sayı = daha sıkı bağlanma (önce işlenir).
|
||||||
|
//
|
||||||
|
// PARAMETRE: type — sorgulanan token tipi
|
||||||
|
// DÖNÜŞ: uint16_t — öncelik seviyesi (0-18)
|
||||||
|
// KARMAŞIKLIK: O(1) — switch/case (derleyici jump table üretir)
|
||||||
|
//
|
||||||
|
// KULLANIM:
|
||||||
|
// uint16_t prec = TokenPrecedence(current.type);
|
||||||
|
// if (prec >= minPrec) { parseLeftDenotation(left); }
|
||||||
//
|
//
|
||||||
// ÖNCELİK SEVİYELERİ (yüksekten düşüğe):
|
// ÖNCELİK SEVİYELERİ (yüksekten düşüğe):
|
||||||
// 18: Üye erişimi . -> [ ] ( )
|
// 18: Üye erişimi . -> [ ] ( ) — En yüksek
|
||||||
// 17: Postfix ++ --
|
// 17: Postfix ++ --
|
||||||
// 16: Unary prefix ! ~
|
// 16: Unary prefix ! ~ + -
|
||||||
// 15: Üs alma ** ^
|
// 15: Üs alma ** ^ — Sağ birleşmeli
|
||||||
// 14: Çarpma/Bölme * / %
|
// 14: Çarpma/Bölme * / %
|
||||||
// 13: Toplama/Çıkarma + -
|
// 13: Toplama/Çıkarma + -
|
||||||
// 12: Bitsel kaydırma << >>
|
// 12: Bitsel kaydırma << >>
|
||||||
// 11: İlişkisel < <= > >=
|
// 11: İlişkisel < <= > >=
|
||||||
// 10: Eşitlik == !=
|
// 10: Eşitlik == !=
|
||||||
// 9: Bitsel VE &
|
// 9: Bitsel VE &
|
||||||
// 8: Bitsel XOR ^ (üs olarak 15'te de var — bağlama göre)
|
// 8: Bitsel XOR ^ (şu anda üs olarak 15'te)
|
||||||
// 7: Bitsel VEYA |
|
// 7: Bitsel VEYA |
|
||||||
// 6: Mantıksal VE &&
|
// 6: Mantıksal VE &&
|
||||||
// 5: Mantıksal VEYA ||
|
// 5: Mantıksal VEYA ||
|
||||||
// 4: Ternary ?
|
// 4: Ternary ?
|
||||||
// 3: Ternary else :
|
// 3: Ternary else :
|
||||||
// 2: Atama = += -= vb.
|
// 2: Atama = += -= vb. — Sağ birleşmeli
|
||||||
// 1: Virgül ,
|
// 1: Virgül ,
|
||||||
// 0: Önceliksiz (değerler, EOF, bilinmeyen)
|
// 0: Önceliksiz (değerler, EOF, bilinmeyen)
|
||||||
//
|
//
|
||||||
// NOT: C/C++'da ^ operatörü bitsel XOR'tur (seviye 8), ama Python'da üs (seviye 15).
|
// KARAR: Neden ^ (CARET) seviye 15 (üs) olarak ayarlı?
|
||||||
// saQut'ta ^ hem üs hem XOR olarak kullanılabilir (AST'de bağlam belirler).
|
// - C/C++'da ^ bitsel XOR'tur (seviye 8).
|
||||||
// Şimdilik ^ seviye 15 (üs) olarak ayarlı.
|
// - Python'da ** üs, ^ XOR'tur.
|
||||||
|
// - saQut'ta ^ varsayılan olarak üs alma olarak kullanılır.
|
||||||
|
// - Gelecekte XOR için ayrı token (CARET_CARET ^^) eklenebilir.
|
||||||
//
|
//
|
||||||
// BUG FIX (commit 438bc0e): Seviye 8'deki ölü kod (CARET için case olmadan
|
// BUG FIX (commit 438bc0e):
|
||||||
// return 8) temizlendi. CARET zaten seviye 15'te STAR_STAR ile birlikte
|
// Seviye 8'de CARET için ölü kod (case olmadan return 8) vardı.
|
||||||
// işleniyor.
|
// Temizlendi. CARET zaten seviye 15'te STAR_STAR ile birlikte işleniyor.
|
||||||
//
|
//
|
||||||
inline uint16_t TokenPrecedence(TokenType type) {
|
inline uint16_t TokenPrecedence(TokenType type) {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
|
|
@ -675,18 +850,29 @@ inline uint16_t TokenPrecedence(TokenType type) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// RightAssociative — Sağdan Sola Birleşme Kontrolü
|
// RightAssociative — Sağdan Sola Birleşme (Associativity) Kontrolü
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
//
|
//
|
||||||
// Hangi operatörler sağdan sola birleşir?
|
// AMAÇ: Bir operatörün sağdan sola mı, yoksa soldan sağa mı birleştiğini
|
||||||
|
// belirler. Pratt parser'da doğru ağaç yapısını oluşturmak için kritik.
|
||||||
|
//
|
||||||
|
// PARAMETRE: type — sorgulanan operatör tipi
|
||||||
|
// DÖNÜŞ: bool — true: sağ birleşmeli, false: sol birleşmeli
|
||||||
|
// KARMAŞIKLIK: O(1) — switch/case
|
||||||
//
|
//
|
||||||
// Sağ birleşmeli operatörler (a OP b OP c = a OP (b OP c)):
|
// Sağ birleşmeli operatörler (a OP b OP c = a OP (b OP c)):
|
||||||
// - Üs alma: **, ^ (matematiksel: 2^3^2 = 2^(3^2) = 2^9 = 512)
|
// - STAR_STAR (üs alma): 2 ** 3 ** 2 = 2 ** (3 ** 2) = 2^9 = 512
|
||||||
// - Atama: =, +=, -=, vb. (a = b = 5 → a = (b = 5))
|
// (matematiksel kural: üs sağdan sola birleşir)
|
||||||
// - Ternary: ?: (a ? b : c ? d : e → a ? b : (c ? d : e))
|
// - CARET (üs alma): 2 ^ 3 ^ 2 = 2 ^ (3 ^ 2) = 512
|
||||||
|
// - EQUAL (atama): a = b = 5 → a = (b = 5)
|
||||||
|
// (önce b = 5 çalışır, sonra a = b)
|
||||||
|
// - +=, -=, *=, vb. (birleşik atama): a += b += 5 → a += (b += 5)
|
||||||
|
// - TERNARY (üçlü koşul): a ? b : c ? d : e → a ? b : (c ? d : e)
|
||||||
|
// (iç içe ternary'lerde sağdan sola)
|
||||||
//
|
//
|
||||||
// Sol birleşmeli operatörler (a OP b OP c = (a OP b) OP c):
|
// Sol birleşmeli operatörler (a OP b OP c = (a OP b) OP c):
|
||||||
// - Tüm diğerleri: +, -, *, /, ==, &&, vb.
|
// - Tüm diğerleri: +, -, *, /, ==, &&, ||, vb.
|
||||||
|
// (a + b + c = (a + b) + c, yani önce a+b, sonuç + c)
|
||||||
//
|
//
|
||||||
inline bool RightAssociative(TokenType type) {
|
inline bool RightAssociative(TokenType type) {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
|
|
@ -711,49 +897,100 @@ inline bool RightAssociative(TokenType type) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// ParserToken — Parser'ın Kullandığı Token Yapısı
|
// ParserToken — Parser'ın Kullandığı Token Yapısı (Köprü)
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
//
|
//
|
||||||
// Tokenizer'ın ürettiği ham Token ile Parser'ın ihtiyaç duyduğu anlamsal
|
// AMAÇ: Tokenizer'ın ürettiği ham Token ile Parser'ın ihtiyaç duyduğu
|
||||||
// tipi (TokenType) bir arada tutar.
|
// anlamsal tipi (TokenType) bir arada tutar. İki katman arasında
|
||||||
|
// köprü görevi görür.
|
||||||
//
|
//
|
||||||
// ALANLAR:
|
// ALANLAR:
|
||||||
// token (Token*): Tokenizer'dan gelen orijinal token. Neden pointer?
|
|
||||||
// Çünkü Token polimorfik bir sınıf hiyerarşisidir. Değer kopyası (Token)
|
|
||||||
// object slicing'e neden olur — alt sınıf verileri (NumberToken.isFloat,
|
|
||||||
// StringToken.context) kaybolur.
|
|
||||||
// BUG FIX (commit 40579ca): Eskiden Token token (değer) tutuyordu.
|
|
||||||
//
|
//
|
||||||
// type (TokenType): Token'ın anlamsal tipi. Örn: NUMBER, PLUS, KW_IF.
|
// token (Token*):
|
||||||
|
// Tokenizer'dan gelen orijinal token'a pointer.
|
||||||
|
// Neden pointer, neden değer (Token token) değil?
|
||||||
|
//
|
||||||
|
// Çünkü Token polimorfik bir sınıf hiyerarşisidir:
|
||||||
|
// Token (base)
|
||||||
|
// +-- NumberToken (isFloat, numberValue alanları)
|
||||||
|
// +-- StringToken (context alanı)
|
||||||
|
// +-- IdentifierToken
|
||||||
|
// +-- OperatorToken
|
||||||
|
// +-- DelimiterToken
|
||||||
|
// +-- KeywordToken
|
||||||
|
//
|
||||||
|
// Değer kopyası (Token token) OBJECT SLICING'e neden olur:
|
||||||
|
// NumberToken → Token'a kopyalanırken isFloat, numberValue KAYBOLUR.
|
||||||
|
//
|
||||||
|
// BUG FIX (commit 40579ca):
|
||||||
|
// Eskiden "Token token" (değer) olarak tanımlanmıştı.
|
||||||
|
// NumberToken.isFloat her zaman false dönüyordu çünkü slicing oluyordu.
|
||||||
|
// "Token* token" (pointer) olarak değiştirildi.
|
||||||
|
//
|
||||||
|
// type (TokenType):
|
||||||
|
// Token'ın anlamsal tipi. Örn: NUMBER, PLUS, KW_IF.
|
||||||
|
// Tokeni parselerken parseToken() tarafından atanır.
|
||||||
//
|
//
|
||||||
// METOTLAR:
|
// METOTLAR:
|
||||||
// is(TokenType): Bu token belirtilen tipte mi?
|
// is(TokenType): Tek tip kontrolü (O(1))
|
||||||
// is({...}): Bu token listedeki tiplerden biri mi?
|
// is(initializer_list): Çoklu tip kontrolü (O(k), k = liste boyutu)
|
||||||
// getPowerOperator(): Bu token bir operatör ise önceliğini döndür.
|
// getPowerOperator(): Öncelik sorgulama (O(1), TokenPrecedence'a yönlendirir)
|
||||||
// isRightAssociative(): Bu operatör sağ birleşmeli mi?
|
// isRightAssociative(): Birleşme yönü sorgulama (O(1))
|
||||||
//
|
//
|
||||||
struct ParserToken {
|
struct ParserToken {
|
||||||
Token* token = nullptr; // Tokenizer'dan gelen orijinal token
|
/* ====== Alanlar ====== */
|
||||||
TokenType type = TokenType::SVR_VOID; // Anlamsal tip
|
|
||||||
|
|
||||||
// Tek tip kontrolü
|
// Tokenizer'dan gelen orijinal token pointer'ı.
|
||||||
|
// nullptr olabilir mi? Hayır — geçerli bir token her zaman vardır.
|
||||||
|
// SVR_VOID durumunda token nullptr olabilir (EOF sinyali).
|
||||||
|
Token* token = nullptr;
|
||||||
|
|
||||||
|
// Token'ın anlamsal tipi.
|
||||||
|
// Varsayılan: SVR_VOID (geçersiz/başlangıç değeri).
|
||||||
|
// parseToken() tarafından atanır.
|
||||||
|
TokenType type = TokenType::SVR_VOID;
|
||||||
|
|
||||||
|
/* ====== Kolaylık Metotları ====== */
|
||||||
|
|
||||||
|
// is() — Tek tip kontrolü
|
||||||
|
// PARAMETRE: t — sorgulanan token tipi
|
||||||
|
// DÖNÜŞ: true — bu token t tipinde
|
||||||
|
// KARMAŞIKLIK: O(1)
|
||||||
|
// KULLANIM: if (current.is(TokenType::SEMICOLON)) { ... }
|
||||||
bool is(TokenType t) const {
|
bool is(TokenType t) const {
|
||||||
return type == t;
|
return type == t;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Çoklu tip kontrolü — örn: is({KW_INT, KW_FLOAT, KW_VOID})
|
// is() — Çoklu tip kontrolü
|
||||||
|
// PARAMETRE: types — kontrol edilecek tipler listesi
|
||||||
|
// DÖNÜŞ: true — bu token listedeki tiplerden birine aitse
|
||||||
|
// KARMAŞIKLIK: O(k) — k = liste boyutu
|
||||||
|
// KULLANIM:
|
||||||
|
// if (current.is({KW_INT, KW_FLOAT, KW_VOID})) { ... }
|
||||||
|
// if (current.is({TokenType::SEMICOLON, TokenType::RPAREN})) { ... }
|
||||||
bool is(std::initializer_list<TokenType> types) const {
|
bool is(std::initializer_list<TokenType> types) const {
|
||||||
for (TokenType t : types)
|
for (TokenType t : types)
|
||||||
if (type == t) return true;
|
if (type == t) return true;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Operatör önceliği (Pratt parser için)
|
// getPowerOperator() — Operatör önceliği sorgulama (Pratt parser için)
|
||||||
|
// DÖNÜŞ: uint16_t — öncelik seviyesi (0-18)
|
||||||
|
// KARMAŞIKLIK: O(1) — TokenPrecedence'a yönlendirir
|
||||||
|
// KULLANIM:
|
||||||
|
// uint16_t prec = current.getPowerOperator();
|
||||||
|
// while (prec >= minPrec) { ... parseLeftDenotation(left); }
|
||||||
uint16_t getPowerOperator() const {
|
uint16_t getPowerOperator() const {
|
||||||
return TokenPrecedence(type);
|
return TokenPrecedence(type);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sağ birleşmeli mi?
|
// isRightAssociative() — Birleşme yönü sorgulama
|
||||||
|
// DÖNÜŞ: true — sağ birleşmeli (atama, üs, ternary)
|
||||||
|
// false — sol birleşmeli (toplama, çarpma, vb.)
|
||||||
|
// KARMAŞIKLIK: O(1) — RightAssociative'a yönlendirir
|
||||||
|
// KULLANIM:
|
||||||
|
// bool rightAssoc = current.isRightAssociative();
|
||||||
|
// uint16_t nextPrec = rightAssoc ? prec : prec + 1;
|
||||||
bool isRightAssociative() const {
|
bool isRightAssociative() const {
|
||||||
return RightAssociative(type);
|
return RightAssociative(type);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue