Compare commits
1 Commits
| Author | SHA1 | Date |
|---|---|---|
|
|
14b0eb9a5e |
|
|
@ -1,5 +1,6 @@
|
|||
# saQut Compiler
|
||||
saqut
|
||||
build/
|
||||
*.o
|
||||
*.exe
|
||||
*.out
|
||||
|
|
|
|||
42
compile.sh
42
compile.sh
|
|
@ -1,42 +0,0 @@
|
|||
#!/bin/bash
|
||||
# ============================================================================
|
||||
# saQut Compiler — Derleme Betiği
|
||||
# ============================================================================
|
||||
#
|
||||
# AMAÇ: Projeyi tek komutla derlemek.
|
||||
#
|
||||
# KULLANIM:
|
||||
# ./compile.sh → saqut binary'sini üret
|
||||
# ./saqut → derleyiciyi çalıştır
|
||||
#
|
||||
# DERLEME SÜRECİ:
|
||||
# Tek bir .cpp dosyası (src/main.cpp) tüm header-only kütüphaneleri
|
||||
# include eder. Harici bağımlılık yoktur.
|
||||
#
|
||||
# g++ parametreleri:
|
||||
# -Isrc : include path (header'lar src/ altında)
|
||||
# -std=c++17 : C++17 standardı (std::variant, constexpr, vb.)
|
||||
# -Wall -Wextra : Tüm uyarıları aç
|
||||
# -O0 -g : Optimizasyon kapalı, debug sembolleri açık
|
||||
# -o saqut : Çıktı binary adı
|
||||
#
|
||||
# GELECEK:
|
||||
# - Makefile veya CMakeLists.txt ile daha esnek build
|
||||
# - Release modu: -O2 -DNDEBUG
|
||||
# - Test modu: ayrı bir test binary'si
|
||||
#
|
||||
# ============================================================================
|
||||
|
||||
set -e # Hata durumunda dur
|
||||
|
||||
echo "=== saQut Compiler Build ==="
|
||||
|
||||
g++ src/main.cpp \
|
||||
-Isrc \
|
||||
-std=c++17 \
|
||||
-Wall -Wextra \
|
||||
-O0 -g \
|
||||
-o saqut
|
||||
|
||||
echo "Derleme başarılı: ./saqut"
|
||||
echo "Çalıştırmak için: ./saqut"
|
||||
|
|
@ -0,0 +1,452 @@
|
|||
# saQut Derleyici ve Dil Ekosistemi — Aşamalı Yol Haritası (ROADMAP)
|
||||
|
||||
> **Son güncelleme:** 2026-05-26
|
||||
> **Amaç:** Bu belge, saQut derleyicisinin ve onu çevreleyen araç zincirinin aşamalı olarak nasıl inşa edileceğini, her aşamanın ön koşullarını, net hedeflerini ve hangi derleyici katmanına denk geldiğini tanımlar. Doğrudan GitHub issue'larına dönüştürülebilecek netlikte yazılmıştır.
|
||||
|
||||
---
|
||||
|
||||
## Vizyon (Kısa Hatırlatma)
|
||||
|
||||
saQut bir derleyici değil, bir **derleyici alet çantasıdır.** Amacı en hızlı kodu üretmek değil, derleyicinin kendisini geliştiriciye açmaktır. Her aşama (Lexer, Parser, Optimizer, Compiler) bağımsız, parametrelerle yönlendirilebilen bir modüldür. Dil, geliştiricinin hiç sıkılmadan binlerce fonksiyon yazabileceği, dosya sisteminden HTTP sunucusuna kadar her seviyede kod yazmaya izin veren, ancak aynı zamanda byte seviyesinde kontrol sağlayan, sıfır bağımlılıkla dağıtılabilen bir yapı olacaktır.
|
||||
|
||||
---
|
||||
|
||||
## Genel Bağımlılık Haritası
|
||||
|
||||
Aşağıdaki diyagram, hangi yeteneğin hangi derleyici katmanına bağımlı olduğunu gösterir.
|
||||
|
||||
```
|
||||
Lexer ─────────────────────────────────────────────────────────────────────┐
|
||||
│ │
|
||||
├── TextMate Syntax Highlighting (regex tabanlı, hemen yapılabilir) │
|
||||
│ │
|
||||
└── Tokenizer ──────────────────────────────────────────────────────┐ │
|
||||
│ │ │
|
||||
└── Parser ────────────────────────────────────────────────┐ │ │
|
||||
│ │ │ │
|
||||
├── AST ────────────────────────────────────────┐ │ │ │
|
||||
│ │ │ │ │ │
|
||||
│ ├── AST Analizi (node sayısı, derinlik) │ │ │ │
|
||||
│ ├── Kod Biçimlendirici (pretty printer) │ │ │ │
|
||||
│ └── Dokümantasyon Üretici (/// yorumlar) │ │ │ │
|
||||
│ │ │ │ │
|
||||
└── Sembol Tablosu ─────────────────────────┐ │ │ │ │
|
||||
│ │ │ │ │ │
|
||||
├── Tip Sistemi ──────────────┐ │ │ │ │ │
|
||||
│ │ │ │ │ │ │ │
|
||||
│ ├── LSP Diagnostics │ │ │ │ │ │
|
||||
│ ├── LSP Hover (tip göster) │ │ │ │ │ │
|
||||
│ ├── LSP Completion │ │ │ │ │ │
|
||||
│ └── Linter / Statik Analiz │ │ │ │ │ │
|
||||
│ │ │ │ │ │ │
|
||||
├── LSP Go to Definition ──────┤ │ │ │ │ │
|
||||
├── LSP Find References ───────┘ │ │ │ │ │
|
||||
├── LSP Document Symbols │ │ │ │ │
|
||||
├── LSP Semantic Tokens (renklendirme) │ │ │ │ │
|
||||
└── Kod Haritası (Call Graph) │ │ │ │ │
|
||||
│ │ │ │ │
|
||||
┌───────────────────────────────────────┘ │ │ │ │
|
||||
│ │ │ │ │
|
||||
└── IR (Intermediate Representation) ───┐ │ │ │ │
|
||||
│ │ │ │ │ │
|
||||
├── C Transpile Backend │ │ │ │ │
|
||||
├── Interpreter (Tree-walk / VM) │ │ │ │ │
|
||||
├── QBE / Custom Backend │ │ │ │ │
|
||||
├── Debugger (DAP, DWARF) │ │ │ │ │
|
||||
└── Optimizasyonlar │ │ │ │ │
|
||||
│ │ │ │ │
|
||||
┌────────────────────────────────────────┘ │ │ │ │
|
||||
│ │ │ │ │
|
||||
└── Proje Sistemi / Build Tooling ─────────────┘ │ │ │
|
||||
│ │ │ │
|
||||
├── saqut init, saqut build, saqut test │ │ │
|
||||
├── Paket Yöneticisi │ │ │
|
||||
└── Derleyici Konfigürasyonu (CompilerConfig) │ │ │
|
||||
│ │ │
|
||||
┌────────────────────────────────────────────────────────────┘ │ │
|
||||
│ │ │
|
||||
└── LSP Sunucusu (JSON-RPC) ───────────────────────────────────────┘ │
|
||||
│ │
|
||||
├── VSCode Eklentisi │
|
||||
├── Diğer IDE Desteği (Neovim, JetBrains, Emacs...) │
|
||||
└── Debug Adapter Protocol (DAP) │
|
||||
```
|
||||
|
||||
> **Okuma rehberi:** Yukarıdaki haritada her bileşen, altındaki bileşenlere bağımlıdır. Örneğin LSP Semantic Tokens için Sembol Tablosu şarttır, Sembol Tablosu için AST şarttır, AST için Parser şarttır.
|
||||
|
||||
---
|
||||
|
||||
# Aşama 0: Metadata ve Konum Takibi (Derleyici Katmanı: Tümü)
|
||||
|
||||
> **Ön Koşul:** Yok. Mevcut kod tabanı üzerine inşa edilir.
|
||||
> **Hedef:** Her token ve AST düğümü kaynak koddaki konumunu bilsin. Hata mesajları satır/sütun gösterebilsin.
|
||||
> **Neden bu aşama ilk?** LSP dahil tüm geliştirici deneyimi özellikleri, konum bilgisine bağımlıdır.
|
||||
|
||||
### Issue 0.1: `SourceFile` ve `SourceLocation` Yapılarını Tanımla
|
||||
- **Dosya:** `src/core/location.hpp` (yeni)
|
||||
- **Yapılacaklar:**
|
||||
- `SourceLocation` yapısı: `fileId` (string), `line` (1 tabanlı), `column` (1 tabanlı), `offset` (0 tabanlı).
|
||||
- `SourceFile` sınıfı: Dosya adı, tam metin, satır başı offset'lerinin listesini tutar.
|
||||
- `offsetToLocation(offset) -> SourceLocation` metodu: Binary search ile offset'i satır/sütuna çevirir.
|
||||
- `locationToOffset(SourceLocation) -> int` metodu: Satır/sütunu offset'e çevirir (doğrulama için).
|
||||
- **Test kriteri:** `SourceFile("a\nbc\n")` için offset 2'nin `{line:2, column:1}` olduğunu doğrula.
|
||||
|
||||
### Issue 0.2: Lexer'a Satır/Sütun Takibi Ekle
|
||||
- **Dosya:** `src/lexer/lexer.hpp`
|
||||
- **Yapılacaklar:**
|
||||
- `Lexer` sınıfına `currentLine`, `currentColumn` üye değişkenleri ekle.
|
||||
- `nextChar()` çağrıldığında `'\n'` görürse satırı artır, sütunu sıfırla.
|
||||
- `skipWhiteSpace()` içinde de satır takibi çalışsın.
|
||||
- Mevcut konumu `SourceLocation` olarak döndüren `getLocation()` metodu ekle.
|
||||
- `INumber` yapısına `startLoc` ve `endLoc` alanları ekle, `readNumeric()` bu alanları doldursun.
|
||||
- **Test kriteri:** `"1 + 2"` tarandığında `1` token'ının konumu `{line:1, column:1}` olmalı.
|
||||
|
||||
### Issue 0.3: Token'lara ve AST Düğümlerine Konum Bilgisi Ekle
|
||||
- **Dosyalar:** `src/tokenizer/token.hpp`, `src/parser/ast.hpp`
|
||||
- **Yapılacaklar:**
|
||||
- `Token` temel sınıfına `SourceLocation loc` ekle. `start`/`end` offset'lerini kaldır (artık `loc` kullan).
|
||||
- `ParserToken` yapısına `SourceLocation loc` ekle, `parseToken()` içinde token'dan kopyala.
|
||||
- `ASTNode` temel sınıfına `SourceLocation loc` ekle. Her düğüm oluşturulurken ilgili token'ın konumunu ata.
|
||||
- **Test kriteri:** `saqut tokens source.sqt` çıktısında her token için satır:sütun bilgisi görünsün.
|
||||
|
||||
---
|
||||
|
||||
# Aşama 1: Sembol Tablosu ve Tip Sisteminin Temelleri (Derleyici Katmanı: Parser sonrası)
|
||||
|
||||
> **Ön Koşul:** Aşama 0 tamamlanmış olmalı.
|
||||
> **Hedef:** Derleyici, kodda tanımlanan her ismi (değişken, fonksiyon, struct) ve tipini bilsin. LSP'nin sorduğu temel sorulara cevap verebilsin.
|
||||
|
||||
### Issue 1.1: Sembol ve Sembol Tablosu Sınıflarını Yaz
|
||||
- **Dosya:** `src/symbol/symbol.hpp`, `src/symbol/symbol_table.hpp` (yeni)
|
||||
- **Yapılacaklar:**
|
||||
- `Symbol` yapısı: `name` (string), `kind` (enum: Variable, Function, Struct, Parameter), `type` (`Type*`), `definitionLoc` (SourceLocation), `references` (vector<SourceLocation>).
|
||||
- `SymbolTable` sınıfı: İç içe scope desteği. `enterScope()`, `exitScope()`, `define(name, Symbol)`, `resolve(name) -> Symbol*`, `addReference(name, location)`.
|
||||
- Scope chain: Global → Function → Block. İsim çözümleme en iç scope'tan dışa doğru.
|
||||
- **Test kriteri:** İç içe bloklarda aynı isimli değişken gölgeleme (shadowing) çalışsın.
|
||||
|
||||
### Issue 1.2: Tip Sistemi Temellerini Kur
|
||||
- **Dosya:** `src/symbol/type.hpp` (yeni)
|
||||
- **Yapılacaklar:**
|
||||
- `Type` soyut sınıfı ve alt sınıfları: `PrimitiveType` (Int, Float, Bool, Char, String, Void), `StructType`, `FunctionType` (parametre tipleri + dönüş tipi), `ArrayType`.
|
||||
- Tip eşitliği kontrolü (`equals` metodu).
|
||||
- Tip uyumluluğu (atama için implicit cast kuralları).
|
||||
- **Test kriteri:** `int x = "hello";` için "tip uyuşmazlığı" hatası alınsın.
|
||||
|
||||
### Issue 1.3: Sembol Toplayıcıyı (SymbolCollector) AST Walker Olarak Yeniden Yaz
|
||||
- **Dosya:** `src/symbol/symbol_collector.hpp` (yeni, mevcut `json.hpp` içindeki `collectSymbols` kaldırılacak)
|
||||
- **Yapılacaklar:**
|
||||
- `SymbolCollector` sınıfı, `ASTNode` ziyaretçisi. AST'yi dolaşır:
|
||||
- `FunctionDecl` → `SymbolTable::define(name, FunctionSymbol)`
|
||||
- `VariableDecl` → `SymbolTable::define(name, VariableSymbol)`, tipini `Type` sisteminden çözümle.
|
||||
- `Identifier` referansları → `SymbolTable::addReference(name, loc)`
|
||||
- `StructDecl` → `SymbolTable::define(name, StructSymbol)`
|
||||
- Ziyaret sırasında tanımsız değişken, çift tanım hatalarını topla.
|
||||
- **Test kriteri:** `saqut symbols source.sqt` çıktısı `SymbolTable`'dan gelen tam veriyi JSON olarak göstersin.
|
||||
|
||||
---
|
||||
|
||||
# Aşama 2: Temel LSP Altyapısı (Derleyici Katmanı: Tümü, yeni `lsp` modu)
|
||||
|
||||
> **Ön Koşul:** Aşama 0 ve Aşama 1 tamamlanmış olmalı.
|
||||
> **Hedef:** VSCode içinde `.sqt` dosyalarında hataları görme, sembollere gitme, imleç altı bilgisi.
|
||||
|
||||
### Issue 2.1: JSON-RPC 2.0 Altyapısını Yaz
|
||||
- **Dosya:** `src/lsp/json_rpc.hpp` (yeni)
|
||||
- **Yapılacaklar:**
|
||||
- `JsonRpcMessage` yapısı: `jsonrpc`, `id`, `method`, `params` alanları.
|
||||
- `parseMessage(string) -> JsonRpcMessage` fonksiyonu: Ham stdin girdisini JSON-RPC mesajına çevirir (sadece ihtiyaç duyulan alanlar).
|
||||
- `serializeMessage(JsonRpcMessage) -> string` fonksiyonu: Cevap mesajını JSON-RPC formatında üretir.
|
||||
- Content-Length header'ı ile çerçeveleme (LSP standardı).
|
||||
- **Test kriteri:** El ile gönderilen `{"jsonrpc":"2.0","id":1,"method":"initialize","params":{...}}` mesajı doğru parse edilsin.
|
||||
|
||||
### Issue 2.2: `saqut lsp` Komutunu Ekle
|
||||
- **Dosya:** `src/cli/commands/lsp.hpp` (yeni), `main.cpp`'ye kayıt ekle
|
||||
- **Yapılacaklar:**
|
||||
- `cmdLsp` fonksiyonu: stdin/stdout'tan JSON-RPC mesajlarını oku, işle, cevap ver.
|
||||
- Ana döngü: `initialize` → `initialized` → `textDocument/didOpen`, `didChange`, `definition`, `hover`...
|
||||
- `shutdown` ve `exit` mesajlarını işle.
|
||||
- **Test kriteri:** `saqut lsp` process'i başlatıldığında `initialize` isteğine `capabilities` ile cevap versin.
|
||||
|
||||
### Issue 2.3: Artımlı Derleme (Incremental Compilation) Desteği Ekle
|
||||
- **Dosya:** `src/lsp/project_manager.hpp` (yeni)
|
||||
- **Yapılacaklar:**
|
||||
- `ProjectManager` sınıfı: Açık dosyaları ve onların AST/Sembol Tablosu kopyalarını tutar.
|
||||
- `didOpen` / `didChange` ile dosya içeriği güncellendiğinde sadece o dosyayı yeniden parse et, sembol tablosunu güncelle.
|
||||
- Hata listesini (diagnostics) güncelle.
|
||||
- **Test kriteri:** Bir dosyada değişiklik yapıldığında sadece o dosyanın yeniden derlenmesi, diğer dosyaların etkilenmemesi.
|
||||
|
||||
### Issue 2.4: İlk LSP Yeteneklerini Uygula
|
||||
- **Dosya:** `src/lsp/handlers/` (yeni dizin)
|
||||
- **Yapılacaklar:**
|
||||
- `textDocument/publishDiagnostics`: Sembol toplayıcıdan gelen hataları LSP formatında bildir.
|
||||
- `textDocument/definition`: İmleç konumundaki sembolü bul, tanımlandığı `SourceLocation`'ı döndür.
|
||||
- `textDocument/documentSymbol`: Dosyadaki tüm sembolleri hiyerarşik olarak listele.
|
||||
- `textDocument/hover`: İmleç altındaki sembolün tipini ve tanımını göster.
|
||||
- **Test kriteri:** VSCode'da `.sqt` dosyası açıldığında hatalar kırmızı alt çizgi ile gösterilsin, Ctrl+Tık tanıma gitsin.
|
||||
|
||||
---
|
||||
|
||||
# Aşama 3: Syntax Highlighting (TextMate + Semantik)
|
||||
|
||||
> **Ön Koşul:** TextMate için: sadece keyword listesi (hemen yapılabilir). Semantik için: Aşama 1 (Sembol Tablosu).
|
||||
> **Hedef:** `.sqt` dosyaları VSCode'da doğru ve zengin renklendirme alsın.
|
||||
|
||||
### Issue 3.1: TextMate Dilbilgisi Dosyası Oluştur
|
||||
- **Dosya:** `editors/vscode/syntaxes/saqut.tmLanguage.json` (yeni)
|
||||
- **Yapılacaklar:**
|
||||
- Keyword'ler, operatörler, sayılar, string'ler, yorum satırları için regex desenleri.
|
||||
- Mevcut `keywords[]` dizisindeki tüm keyword'leri kapsa.
|
||||
- String interpolation (ileride) için temel yapı.
|
||||
- **Test kriteri:** VSCode'da `.sqt` dosyası açıldığında `if`, `for` mavi, `"hello"` turuncu, `// yorum` yeşil görünsün.
|
||||
|
||||
### Issue 3.2: Semantik Token Desteği Ekle
|
||||
- **Dosya:** `src/lsp/handlers/semantic_tokens.hpp` (yeni)
|
||||
- **Yapılacaklar:**
|
||||
- `textDocument/semanticTokens/full` isteğini işle.
|
||||
- AST + Sembol Tablosu kullanarak her token için `tokenType` (function, variable, parameter, type, keyword) ve `tokenModifiers` (declaration, readonly, static) belirle.
|
||||
- LSP semantic token delta formatında kodla ve döndür.
|
||||
- **Test kriteri:** VSCode'da `int x = foo();` yazıldığında `x` (değişken) ile `foo` (fonksiyon) farklı renkte olsun.
|
||||
|
||||
---
|
||||
|
||||
# Aşama 4: Kod Biçimlendirici ve Dokümantasyon Üretici (Derleyici Katmanı: AST)
|
||||
|
||||
> **Ön Koşul:** Aşama 0, Aşama 1. (AST ve konum bilgisi yeterli)
|
||||
> **Hedef:** `saqut fmt` ile otomatik formatlama, `saqut doc` ile dokümantasyon üretimi.
|
||||
|
||||
### Issue 4.1: Pretty Printer (Kod Biçimlendirici)
|
||||
- **Dosya:** `src/tools/pretty_printer.hpp` (yeni)
|
||||
- **Yapılacaklar:**
|
||||
- AST'yi dolaşarak, standart girintileme ve boşluk kurallarıyla kaynak kod üret.
|
||||
- `saqut fmt <dosya>` komutu: dosyayı oku, parse et, pretty print yap, orijinal dosyayı güncelle (veya stdout'a bas).
|
||||
- Kural seti: 4 boşluk girinti, operatör etrafında boşluk, virgülden sonra boşluk, süslü parantez aynı satırda.
|
||||
- **Test kriteri:** `saqut fmt source.sqt` çalıştırıldığında dosya tutarlı formata dönüşsün, aynı kod tekrar formatlanırsa değişmesin (idempotent).
|
||||
|
||||
### Issue 4.2: Dokümantasyon Üretici
|
||||
- **Dosya:** `src/tools/doc_generator.hpp` (yeni)
|
||||
- **Yapılacaklar:**
|
||||
- `///` veya `/** */` ile başlayan yorum bloklarını AST düğümlerine bağla (önceki satırdaki yorum, sonraki fonksiyon/değişken için doc sayılır).
|
||||
- `saqut doc <dosya>` komutu: Markdown formatında API dokümantasyonu üret.
|
||||
- LSP `hover` isteğine bu dokümantasyonu ekle (zengin Markdown).
|
||||
- **Test kriteri:** `/// Bu fonksiyon iki sayıyı toplar` yorumu, `int add(int a, int b)` için hover'da görünsün.
|
||||
|
||||
---
|
||||
|
||||
# Aşama 5: IR Güçlendirme ve Interpreter (Derleyici Katmanı: IR + Backend)
|
||||
|
||||
> **Ön Koşul:** Aşama 1 (Sembol Tablosu ve Tip Sistemi).
|
||||
> **Hedef:** Yazılan kodun çalıştırılabilmesi. Hem interpreter (REPL + dosya) hem de C transpile.
|
||||
|
||||
### Issue 5.1: IR'ye Kontrol Akışı ve Fonksiyon Opcode'ları Ekle
|
||||
- **Dosya:** `src/ir/ir.hpp`
|
||||
- **Yapılacaklar:**
|
||||
- `OPCode` enum'una ekle: `br` (koşulsuz dal), `br_eq`, `br_lt`, `br_gt` (koşullu dal), `cmp` (karşılaştır), `call`, `ret`, `param`, `load`, `store`, `alloca`.
|
||||
- `IROpData` yapısını yeni opcode'lar için güncelle (hedef adres, koşul register'ları).
|
||||
- `CodeGenerator` sınıfına `if`, `while`, `for`, `do-while`, fonksiyon çağrısı, return için IR üretme mantığını ekle.
|
||||
- **Test kriteri:** `if (x < 5) return x;` kodu için `cmp`, `br_lt`, `ret` IR komutları üretilsin.
|
||||
|
||||
### Issue 5.2: Interpreter (Sanal Makine)
|
||||
- **Dosya:** `src/backend/interpreter.hpp` (yeni)
|
||||
- **Yapılacaklar:**
|
||||
- `Interpreter` sınıfı: IR komutlarını sırayla yürütür.
|
||||
- Sanal register'ları ve bellek (stack) yönetimi.
|
||||
- `OPCode` başına yürütme mantığı (switch-case).
|
||||
- `saqut run <dosya>` komutu: Interpreter'ı kullanarak kodu çalıştır.
|
||||
- `saqut` (argümansız): REPL modu, her satırı anında yorumla.
|
||||
- **Test kriteri:** `int main() { return 42; }` kodu çalıştırıldığında çıkış kodu 42 olsun.
|
||||
|
||||
### Issue 5.3: C Transpile Backend
|
||||
- **Dosya:** `src/backend/transpile_c.hpp` (yeni)
|
||||
- **Yapılacaklar:**
|
||||
- `CTranspiler` sınıfı: IR komutlarını C koduna çevirir.
|
||||
- `saqut transpile <dosya> -o output.c` komutu.
|
||||
- Üretilen C kodu `gcc`/`clang` ile derlenebilir ve çalıştırılabilir olmalı.
|
||||
- `#line` direktifleri ile orijinal kaynak koda geri referans ver.
|
||||
- **Test kriteri:** `int main() { return 1+2; }` kodu C'ye çevrilip GCC ile derlendiğinde çalışan binary 3 çıkış kodu versin.
|
||||
|
||||
---
|
||||
|
||||
# Aşama 6: Debugger (DAP Tabanlı)
|
||||
|
||||
> **Ön Koşul:** Aşama 5 (Interpreter).
|
||||
> **Hedef:** VSCode içinden breakpoint koyma, adım adım çalıştırma, değişken izleme.
|
||||
|
||||
### Issue 6.1: Debug Adapter Protocol (DAP) Altyapısı
|
||||
- **Dosya:** `src/debug/dap_server.hpp` (yeni)
|
||||
- **Yapılacaklar:**
|
||||
- JSON-RPC üzerine DAP mesajlarını işleyen sunucu (JSON-RPC altyapısı zaten LSP'den var, ortak kullan).
|
||||
- `saqut debug` komutu: DAP sunucusunu başlatır.
|
||||
- `launch` ve `attach` isteklerini işle.
|
||||
- **Test kriteri:** VSCode'dan "Debug" başlatıldığında DAP el sıkışması (handshake) başarılı olsun.
|
||||
|
||||
### Issue 6.2: Interpreter'a Debugging Yetenekleri Ekle
|
||||
- **Dosya:** `src/backend/interpreter.hpp` (güncelle)
|
||||
- **Yapılacaklar:**
|
||||
- Interpreter'a `stepOver`, `stepInto`, `stepOut`, `pause`, `continue` yetenekleri.
|
||||
- Breakpoint yönetimi: `SourceLocation` ile eşleşen satırlarda dur.
|
||||
- Değişken değerlerini okuma: `stackFrame`'den sembol ismiyle değer bulma.
|
||||
- `variables` ve `evaluate` isteklerini cevapla.
|
||||
- **Test kriteri:** VSCode'da bir satıra breakpoint koyup F5 ile debug başlatıldığında, o satırda dursun ve değişken değerleri gösterilsin.
|
||||
|
||||
### Issue 6.3: Zaman Yolculuğu Debugging (Opsiyonel, İleri Seviye)
|
||||
- **Dosya:** `src/debug/time_travel.hpp` (yeni)
|
||||
- **Yapılacaklar:**
|
||||
- Interpreter her adımda program durumunu (stack, register'lar) bir listeye kaydetsin.
|
||||
- Geri adım (`stepBack`) desteği.
|
||||
- `saqut debug --time-travel` ile açılır.
|
||||
- **Test kriteri:** Debug sırasında "Step Back" yapıldığında bir önceki satıra dönülsün ve değişken eski değerini göstersin.
|
||||
|
||||
---
|
||||
|
||||
# Aşama 7: Gelişmiş LSP Yetenekleri (Derleyici Katmanı: Tümü)
|
||||
|
||||
> **Ön Koşul:** Aşama 1, Aşama 5 (Interpreter çalışıyor olmalı).
|
||||
> **Hedef:** VSCode'da tam kod tamamlama, referans bulma, kod haritası.
|
||||
|
||||
### Issue 7.1: Kod Tamamlama (Completion)
|
||||
- **Dosya:** `src/lsp/handlers/completion.hpp`
|
||||
- **Yapılacaklar:**
|
||||
- `textDocument/completion` isteğini işle.
|
||||
- İmleç konumundaki scope'a göre görünür tüm sembolleri listele.
|
||||
- Fonksiyon parametre bilgisi (signature help).
|
||||
- **Test kriteri:** `x.` yazıldığında `x`'in struct üyeleri listelensin.
|
||||
|
||||
### Issue 7.2: Referans Bulma (Find References)
|
||||
- **Dosya:** `src/lsp/handlers/references.hpp`
|
||||
- **Yapılacaklar:**
|
||||
- `textDocument/references` isteğini işle.
|
||||
- Sembol tablosundan sembolün tüm referans konumlarını döndür.
|
||||
- **Test kriteri:** Bir değişkene sağ tıklanıp "Find All References" dendiğinde tüm kullanımlar listelensin.
|
||||
|
||||
### Issue 7.3: Kod Haritası / Çağrı Grafiği (Call Hierarchy)
|
||||
- **Dosya:** `src/lsp/handlers/call_hierarchy.hpp`
|
||||
- **Yapılacaklar:**
|
||||
- `textDocument/prepareCallHierarchy`, `callHierarchy/incomingCalls`, `callHierarchy/outgoingCalls` isteklerini işle.
|
||||
- AST ve Sembol Tablosu'ndan çağrı ilişkilerini çıkar.
|
||||
- **Test kriteri:** Bir fonksiyona sağ tıklanıp "Show Call Hierarchy" dendiğinde çağrı ağacı gösterilsin.
|
||||
|
||||
---
|
||||
|
||||
# Aşama 8: Optimizasyonlar ve Analiz Araçları
|
||||
|
||||
> **Ön Koşul:** Aşama 5 (IR ve Interpreter).
|
||||
> **Hedef:** Kod kalitesini artıran, geliştiriciye içgörü sağlayan araçlar.
|
||||
|
||||
### Issue 8.1: Linter ve Statik Analiz
|
||||
- **Dosya:** `src/tools/linter.hpp`
|
||||
- **Yapılacaklar:**
|
||||
- `saqut lint <dosya>` komutu.
|
||||
- Kullanılmayan değişken, import, fonksiyon tespiti.
|
||||
- Karmaşıklık metrikleri (cyclomatic complexity).
|
||||
- Null olabilecek değerlere erişim kontrolü (basit veri akışı analizi).
|
||||
- **Test kriteri:** Kullanılmayan bir değişken için `[WARN] Unused variable 'x' at line 5` çıktısı alınsın.
|
||||
|
||||
### Issue 8.2: Optimizasyon Pas'ları
|
||||
- **Dosya:** `src/optimizer/optimizer.hpp`, `src/optimizer/passes/`
|
||||
- **Yapılacaklar:**
|
||||
- Constant folding: `4 + 1` → `5`.
|
||||
- Dead code elimination: `if (false) { ... }` bloğunu sil.
|
||||
- Unused variable elimination: Kullanılmayan değişken tanımlarını kaldır.
|
||||
- `CompilerConfig` ile her pas açılıp kapatılabilir olmalı.
|
||||
- **Test kriteri:** `--opt-all` ile çalıştırılan kod, optimizasyonsuz halinden daha az IR komutu üretsin.
|
||||
|
||||
### Issue 8.3: Performans Profili (Profiler)
|
||||
- **Dosya:** `src/tools/profiler.hpp`
|
||||
- **Yapılacaklar:**
|
||||
- `saqut profile run <dosya>` komutu.
|
||||
- Interpreter modunda her fonksiyon çağrısının süresini ölç.
|
||||
- Sonuçları flame graph formatında (katlanabilir JSON) dışa aktar.
|
||||
- **Test kriteri:** Profiler çıktısı, en çok zaman harcanan fonksiyonu belirtsin.
|
||||
|
||||
---
|
||||
|
||||
# Aşama 9: Proje Sistemi, Derleyici Konfigürasyonu ve Feature Toggle
|
||||
|
||||
> **Ön Koşul:** Aşama 5 (Interpreter), Aşama 8 (Optimizasyon altyapısı).
|
||||
> **Hedef:** `saqut init`, `saqut build`, `saqut test` komutlarının çalışması.
|
||||
|
||||
### Issue 9.1: Proje Başlatma ve Build Sistemi
|
||||
- **Dosya:** `src/cli/commands/init.hpp`, `src/cli/commands/build.hpp`
|
||||
- **Yapılacaklar:**
|
||||
- `saqut init <proje-adı>`: Standart dizin yapısı ve `saqut.json` (proje manifesti) oluştur.
|
||||
- `saqut build`: Manifest'i oku, tüm kaynak dosyaları derle, tek bir çalıştırılabilir üret (C transpile + GCC veya interpreter bytecode).
|
||||
- **Test kriteri:** `saqut init myapp && cd myapp && saqut build` ile çalışan bir program elde edilsin.
|
||||
|
||||
### Issue 9.2: CompilerConfig ve Feature Toggle
|
||||
- **Dosya:** `src/core/compiler_config.hpp`
|
||||
- **Yapılacaklar:**
|
||||
- `CompilerConfig` yapısı: Tüm dil özellikleri (`enableWhile`, `enableFor`, `enableClass`...), optimizasyon pas'ları, çıktı formatı.
|
||||
- `saqut` komut satırından `--disable-while`, `--skip-constant-folding` gibi bayraklarla konfigüre edilebilsin.
|
||||
- Tokenizer ve Parser, `CompilerConfig`'e göre davranış değiştirsin (kapalı keyword'leri identifier olarak tanısın).
|
||||
- **Test kriteri:** `saqut run --disable-while` ile while döngüsü içeren kod hata versin.
|
||||
|
||||
### Issue 9.3: Test Çerçevesi (Built-in)
|
||||
- **Dosya:** `src/cli/commands/test.hpp`, `src/core/test_framework.hpp`
|
||||
- **Yapılacaklar:**
|
||||
- `test` anahtar kelimesi veya `#[test]` anotasyonu.
|
||||
- `saqut test` komutu: Projedeki tüm test fonksiyonlarını bul, çalıştır, sonuçları raporla.
|
||||
- Test sonuçları TAP ve JUnit XML formatında çıkış.
|
||||
- **Test kriteri:** `test "iki kere iki dort" { assert(2*2 == 4); }` testi `saqut test` ile geçsin.
|
||||
|
||||
---
|
||||
|
||||
# Aşama 10: Paket Yöneticisi ve Dağıtım (Gelecek)
|
||||
|
||||
> **Ön Koşul:** Aşama 9 (Proje sistemi).
|
||||
> **Hedef:** Harici kütüphaneleri projeye ekleme ve dilin resmi paket kaydı.
|
||||
|
||||
### Issue 10.1: Paket Yöneticisi (saqut add)
|
||||
- **Dosya:** `src/cli/commands/add.hpp`, `src/package/registry.hpp`
|
||||
- **Yapılacaklar:**
|
||||
- `saqut add <paket-adi>`: Merkezi paket kaydından (GitHub tabanlı) paketi indir, `saqut.json`'a ekle.
|
||||
- Bağımlılık çözümleme (dependency resolution).
|
||||
- **Test kriteri:** `saqut add json` ile JSON kütüphanesi projeye eklensin ve `import json;` kullanılabilsin.
|
||||
|
||||
### Issue 10.2: Kendi Kendini Derleme (Self-Hosting)
|
||||
- **Dosya:** Proje geneli.
|
||||
- **Yapılacaklar:**
|
||||
- saQut derleyicisinin kaynak kodu, saQut dilinde yazılabilsin ve saQut ile derlenebilsin.
|
||||
- Bu, dilin olgunluğunun nihai testidir.
|
||||
- **Test kriteri:** `saqut build` ile derleyici kendi kaynak kodunu derleyip çalışan bir binary üretsin.
|
||||
|
||||
---
|
||||
|
||||
# Aşama 11: Dil Spesifikasyonu ve Resmi Dokümantasyon
|
||||
|
||||
> **Ön Koşul:** Aşama 9 (dil özellikleri stabilize olmalı).
|
||||
> **Hedef:** Geliştiricilerin referans alabileceği eksiksiz bir dil tanımı.
|
||||
|
||||
### Issue 11.1: Dil Spesifikasyonu Yaz
|
||||
- **Dosya:** `docs/lang_spec.md` (kitap formatında)
|
||||
- **Yapılacaklar:**
|
||||
- Sözdizimi grameri (EBNF).
|
||||
- Tip sistemi ve dönüşüm kuralları.
|
||||
- Çalışma zamanı davranışları (bellek modeli, hata yönetimi).
|
||||
- Standart kütüphane sözleşmeleri.
|
||||
- **Test kriteri:** Spesifikasyon, tüm dil özelliklerini kapsasın ve derleyici davranışıyla tutarlı olsun.
|
||||
|
||||
### Issue 11.2: Resmi Web Sitesi ve Öğretici
|
||||
- **Dosya:** `docs/website/`
|
||||
- **Yapılacaklar:**
|
||||
- saqut-lang.org için statik site.
|
||||
- "Başlangıç" rehberi, örnekler, API referansı (doc generator çıktısı).
|
||||
- **Test kriteri:** Site yayında ve tüm örnekler çalıştırılabilir olsun.
|
||||
|
||||
---
|
||||
|
||||
# Ek: VSCode Eklentisi Paketleme
|
||||
|
||||
> **Ön Koşul:** Aşama 3 (Syntax Highlighting), Aşama 4 (LSP handlers).
|
||||
> **Hedef:** VSCode marketplace'te yayınlanabilir bir eklenti.
|
||||
|
||||
### Issue E.1: VSCode Eklenti Manifesti ve Paketleme
|
||||
- **Dosya:** `editors/vscode/package.json`, `editors/vscode/extension.js`
|
||||
- **Yapılacaklar:**
|
||||
- `package.json` içinde `activationEvents`, `contributes.languages`, `contributes.commands` tanımla.
|
||||
- `extension.js`: LSP sunucusunu (`saqut lsp`) başlat, dil konfigürasyonunu yükle.
|
||||
- `.vsix` paketi oluşturma betiği.
|
||||
- **Test kriteri:** Eklenti VSCode'a yüklendiğinde `.sqt` dosyaları tanınsın ve LSP başlatılsın.
|
||||
|
||||
---
|
||||
|
||||
> **Yol haritası sonu.** Her aşamadaki issue'lar sırasıyla yapıldığında, saQut hem kendi kendini derleyebilen bir dil hem de geliştiricisine benzersiz içgörü sağlayan bir araç zinciri haline gelir.
|
||||
18
readme.md
18
readme.md
|
|
@ -1,5 +1,21 @@
|
|||
# Syntax
|
||||
# Proje Yapısı ve Derleme
|
||||
|
||||
## Dizin Yapısı
|
||||
- `src/`: Derleyici kaynak kodları (C++).
|
||||
- `docs/`: Geliştirme notları, roadmap ve issue listesi.
|
||||
- `examples/`: Örnek `.sqt` (saQut) kaynak kodları.
|
||||
- `scripts/`: Derleme ve yardımcı betikler.
|
||||
- `build/`: Derleme sonucu oluşan binary ve çıktılar (git tarafından yoksayılır).
|
||||
|
||||
## Derleme
|
||||
Projeyi derlemek için ana dizinde şu komutu çalıştırabilirsiniz:
|
||||
```bash
|
||||
bash scripts/compile.sh
|
||||
```
|
||||
Derleme başarılı olduktan sonra çalıştırılabilir dosya `build/saqut` konumunda oluşacaktır.
|
||||
|
||||
# Syntax
|
||||
...
|
||||
Syntax modunda 2 seçenek bulunmaktadır
|
||||
- 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
|
||||
|
|
|
|||
|
|
@ -0,0 +1,26 @@
|
|||
#!/bin/bash
|
||||
# ============================================================================
|
||||
# saQut Compiler — Derleme Betiği
|
||||
# ============================================================================
|
||||
|
||||
set -e
|
||||
|
||||
# Betiğin bulunduğu dizini al
|
||||
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"
|
||||
PROJECT_ROOT="$( dirname "$SCRIPT_DIR" )"
|
||||
|
||||
echo "=== saQut Compiler Build ==="
|
||||
|
||||
# Build dizinini oluştur
|
||||
mkdir -p "$PROJECT_ROOT/build"
|
||||
|
||||
# Derleme
|
||||
g++ "$PROJECT_ROOT/src/main.cpp" \
|
||||
-I"$PROJECT_ROOT/src" \
|
||||
-std=c++17 \
|
||||
-Wall -Wextra \
|
||||
-O0 -g \
|
||||
-o "$PROJECT_ROOT/build/saqut"
|
||||
|
||||
echo "Derleme başarılı: build/saqut"
|
||||
echo "Çalıştırmak için: ./build/saqut"
|
||||
|
|
@ -98,7 +98,7 @@ inline CliArgs parseArgs(int argc, char* argv[]) {
|
|||
|
||||
if (args.command.empty()) args.command = "run";
|
||||
if (args.positional.empty() && !args.stdinMode)
|
||||
args.positional.push_back("source.sqt");
|
||||
args.positional.push_back("examples/source.sqt");
|
||||
|
||||
return args;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -90,10 +90,10 @@ public:
|
|||
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";
|
||||
std::cout << " saqut run examples/source.sqt\n";
|
||||
std::cout << " saqut tokens examples/source.sqt\n";
|
||||
std::cout << " saqut ast examples/source.sqt --format=json\n";
|
||||
std::cout << " saqut symbols examples/source.sqt\n";
|
||||
}
|
||||
|
||||
private:
|
||||
|
|
|
|||
|
|
@ -1,45 +1,878 @@
|
|||
// ============================================================================
|
||||
// saQut Compiler — Soyut Sözdizim Ağacı (Aggregator)
|
||||
// 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), JSON (src/json.hpp)
|
||||
//
|
||||
// Bu dosya bir AGGREGATOR'dür. Tüm AST düğüm sınıflarını tek bir include
|
||||
// ile kullanılabilir yapar.
|
||||
// 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) — ast_node.hpp
|
||||
// ├── ProgramNode : Kök düğüm — ast_decl.hpp
|
||||
// ├── FunctionDeclNode : Fonksiyon tanımı — ast_decl.hpp
|
||||
// ├── StructDeclNode : struct tanımı — ast_decl.hpp
|
||||
// ├── VariableDeclNode : Değişken tanımı — ast_decl.hpp
|
||||
// ├── BlockNode : { ... } bloğu — ast_stmt.hpp
|
||||
// ├── IfStatementNode : if/else — ast_stmt.hpp
|
||||
// ├── WhileStatementNode : while — ast_stmt.hpp
|
||||
// ├── ForStatementNode : for — ast_stmt.hpp
|
||||
// ├── DoWhileStatementNode : do-while — ast_stmt.hpp
|
||||
// ├── ReturnStatementNode : return — ast_stmt.hpp
|
||||
// ├── BreakStatementNode : break — ast_stmt.hpp
|
||||
// ├── ContinueStatementNode : continue — ast_stmt.hpp
|
||||
// ├── ExpressionStatementNode: expression; — ast_stmt.hpp
|
||||
// ├── BinaryExpressionNode : a + b — ast_expr.hpp
|
||||
// ├── LiteralNode : 42, "hello" — ast_expr.hpp
|
||||
// ├── IdentifierNode : değişken ismi — ast_expr.hpp
|
||||
// ├── PostfixNode : a++ — ast_expr.hpp
|
||||
// ├── CallExpressionNode : f(x) — ast_expr.hpp
|
||||
// ├── MemberAccessNode : a.b — ast_expr.hpp
|
||||
// └── IndexExpressionNode : a[i] — ast_expr.hpp
|
||||
// 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 + log() + toJson(): Her düğüm kendi tipini bilir,
|
||||
// kendini konsola yazdırabilir ve JSON olarak serileştirebilir.
|
||||
// Yeni bir düğüm eklendiğinde tüm davranışlar tek yerde tanımlanır.
|
||||
//
|
||||
// 2. parent pointer: Her düğüm ebeveynini bilir.
|
||||
//
|
||||
// 3. children vektörü (protected): Liste tipi düğümler için.
|
||||
//
|
||||
// BİLİNEN SINIRLAMALAR (TODO):
|
||||
// TODO: Bellek yönetimi (unique_ptr veya arena allocator)
|
||||
// TODO: Visitor pattern ile log/toJson/IR üretimi ayrıştırılabilir
|
||||
//
|
||||
// ============================================================================
|
||||
|
||||
#ifndef SAQUT_AST
|
||||
#define SAQUT_AST
|
||||
|
||||
#include "parser/ast_node.hpp"
|
||||
#include "parser/ast_json.hpp"
|
||||
#include "parser/ast_expr.hpp"
|
||||
#include "parser/ast_stmt.hpp"
|
||||
#include "parser/ast_decl.hpp"
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
#include "core/location.hpp"
|
||||
#include "parser/token.hpp"
|
||||
#include "tools.hpp"
|
||||
|
||||
// ============================================================================
|
||||
// ASTKind — AST Düğüm Tipi Enum'u
|
||||
// ============================================================================
|
||||
|
||||
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 + ;
|
||||
Call, // Fonksiyon çağrısı f(args)
|
||||
MemberAccess, // Üye erişimi a.b, a->b
|
||||
IndexExpression, // Dizi erişimi a[i]
|
||||
StructDecl, // struct tanımı
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// ASTNode — Soyut Temel Sınıf
|
||||
// ============================================================================
|
||||
|
||||
class ASTNode {
|
||||
public:
|
||||
ASTKind kind;
|
||||
ASTNode* parent = nullptr;
|
||||
SourceLocation loc; // Bu düğümün kaynak koddaki konumu
|
||||
|
||||
virtual void log(int indent = 0) {
|
||||
(void)indent;
|
||||
std::cout << "<Unknown>\n";
|
||||
}
|
||||
|
||||
// JSON serileştirme — her alt sınıf kendi implemente eder
|
||||
virtual std::string toJson(int indent = 0) {
|
||||
(void)indent;
|
||||
return "{\"kind\":\"Unknown\"}";
|
||||
}
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// JSON yardımcısı: alt düğüm listesini JSON array olarak yaz
|
||||
// ============================================================================
|
||||
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 << ",";
|
||||
ss << "\n";
|
||||
}
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// ProgramNode — Kök Düğüm
|
||||
// ============================================================================
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
std::string toJson(int depth = 0) override {
|
||||
std::string in = jsonIndent(depth);
|
||||
std::ostringstream ss;
|
||||
ss << in << "{\n"
|
||||
<< in << " \"kind\": \"Program\",\n"
|
||||
<< in << " \"children\": [\n"
|
||||
<< childrenToJson(this, depth + 3)
|
||||
<< in << " ]\n"
|
||||
<< in << "}";
|
||||
return ss.str();
|
||||
}
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// FunctionDeclNode — Fonksiyon Tanımı
|
||||
// ============================================================================
|
||||
|
||||
class FunctionDeclNode : public ASTNode {
|
||||
public:
|
||||
std::string name;
|
||||
std::string returnType;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
std::string toJson(int depth = 0) override {
|
||||
std::string in = jsonIndent(depth);
|
||||
std::ostringstream ss;
|
||||
ss << in << "{\n"
|
||||
<< in << " \"kind\": \"FunctionDecl\",\n"
|
||||
<< in << " \"name\": \"" << jsonEscape(name) << "\",\n"
|
||||
<< in << " \"returnType\": \"" << jsonEscape(returnType) << "\",\n"
|
||||
<< in << " \"location\": " << loc.toJson() << ",\n"
|
||||
<< in << " \"children\": [\n"
|
||||
<< childrenToJson(this, depth + 3)
|
||||
<< in << " ]\n"
|
||||
<< in << "}";
|
||||
return ss.str();
|
||||
}
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// BlockNode — Blok { ... }
|
||||
// ============================================================================
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
std::string toJson(int depth = 0) override {
|
||||
std::string in = jsonIndent(depth);
|
||||
std::ostringstream ss;
|
||||
ss << in << "{\n"
|
||||
<< in << " \"kind\": \"Block\",\n"
|
||||
<< in << " \"children\": [\n"
|
||||
<< childrenToJson(this, depth + 3)
|
||||
<< in << " ]\n"
|
||||
<< in << "}";
|
||||
return ss.str();
|
||||
}
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// VariableDeclNode — Değişken Tanımı
|
||||
// ============================================================================
|
||||
|
||||
class VariableDeclNode : public ASTNode {
|
||||
public:
|
||||
std::string varType;
|
||||
std::string name;
|
||||
ASTNode* initExpr = nullptr;
|
||||
|
||||
VariableDeclNode() { kind = ASTKind::VariableDecl; }
|
||||
|
||||
std::string toJson(int depth = 0) override {
|
||||
std::string in = jsonIndent(depth);
|
||||
std::ostringstream ss;
|
||||
ss << in << "{\n"
|
||||
<< in << " \"kind\": \"VariableDecl\",\n"
|
||||
<< in << " \"name\": \"" << jsonEscape(name) << "\",\n"
|
||||
<< in << " \"varType\": \"" << jsonEscape(varType) << "\",\n"
|
||||
<< in << " \"location\": " << loc.toJson() << "";
|
||||
if (initExpr) {
|
||||
ss << ",\n" << in << " \"initExpr\":\n"
|
||||
<< initExpr->toJson(depth + 2);
|
||||
}
|
||||
// Çoklu değişken bildirimindeki kardeşler (int a, b, c;)
|
||||
if (!getChildren().empty()) {
|
||||
ss << ",\n" << in << " \"declarators\": [\n";
|
||||
for (size_t i = 0; i < getChildren().size(); i++) {
|
||||
ss << ((VariableDeclNode*)getChildren()[i])->toJson(depth + 2);
|
||||
if (i + 1 < getChildren().size()) ss << ",";
|
||||
ss << "\n";
|
||||
}
|
||||
ss << in << " ]";
|
||||
}
|
||||
ss << "\n" << in << "}";
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
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";
|
||||
}
|
||||
// Kardeş değişkenleri de logla
|
||||
for (auto* child : getChildren()) {
|
||||
child->log(indent);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// BinaryExpressionNode — İkili İşlem (a OP b)
|
||||
// ============================================================================
|
||||
|
||||
class BinaryExpressionNode : public ASTNode {
|
||||
public:
|
||||
TokenType Operator;
|
||||
ASTNode* Left = nullptr;
|
||||
ASTNode* Right = nullptr;
|
||||
|
||||
BinaryExpressionNode() { kind = ASTKind::BinaryExpression; }
|
||||
|
||||
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::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";
|
||||
if (Right) Right->log(indent + 2);
|
||||
if (Left) Left->log(indent + 2);
|
||||
}
|
||||
|
||||
std::string toJson(int depth = 0) override {
|
||||
std::string in = jsonIndent(depth);
|
||||
std::string opSym = "?";
|
||||
auto it = OPERATOR_MAP_REV.find(Operator);
|
||||
if (it != OPERATOR_MAP_REV.end()) opSym = std::string(it->second);
|
||||
|
||||
std::ostringstream ss;
|
||||
ss << in << "{\n"
|
||||
<< in << " \"kind\": \"BinaryExpression\",\n"
|
||||
<< in << " \"operator\": \"" << jsonEscape(opSym) << "\",\n"
|
||||
<< in << " \"location\": " << loc.toJson() << "";
|
||||
if (Left) {
|
||||
ss << ",\n" << in << " \"left\":\n"
|
||||
<< Left->toJson(depth + 2);
|
||||
}
|
||||
if (Right) {
|
||||
ss << ",\n" << in << " \"right\":\n"
|
||||
<< Right->toJson(depth + 2);
|
||||
}
|
||||
ss << "\n" << in << "}";
|
||||
return ss.str();
|
||||
}
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// LiteralNode — Sabit Değer
|
||||
// ============================================================================
|
||||
|
||||
// Literal tipleri
|
||||
enum class LiteralType : uint8_t {
|
||||
INTEGER, // Tamsayı (decimal, hex, octal, binary)
|
||||
FLOAT, // Ondalıklı sayı (3.14, 1e-5)
|
||||
STRING, // Metin ("hello")
|
||||
BOOLEAN, // true / false
|
||||
BOŞ // null
|
||||
};
|
||||
|
||||
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 "?";
|
||||
}
|
||||
|
||||
class LiteralNode : public ASTNode {
|
||||
public:
|
||||
Token* lexerToken = nullptr;
|
||||
ParserToken parserToken;
|
||||
|
||||
LiteralType literalType = LiteralType::INTEGER;
|
||||
int literalBase = 10; // 10, 16, 8, 2 (sadece INTEGER/FLOAT için)
|
||||
bool isFloatValue = false; // Ondalıklı mı? (sadece INTEGER/FLOAT için)
|
||||
|
||||
LiteralNode() { kind = ASTKind::Literal; }
|
||||
|
||||
void log(int indent = 0) override {
|
||||
std::cout << padRight("", indent)
|
||||
<< "Literal {" << parserToken.token->token << "} "
|
||||
<< literalTypeToString(literalType);
|
||||
if (literalType == LiteralType::INTEGER && literalBase != 10)
|
||||
std::cout << " (base " << literalBase << ")";
|
||||
std::cout << "\n";
|
||||
}
|
||||
|
||||
std::string toJson(int depth = 0) override {
|
||||
std::string in = jsonIndent(depth);
|
||||
std::string val = parserToken.token ? parserToken.token->token : "?";
|
||||
std::ostringstream ss;
|
||||
ss << in << "{\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 << " \"location\": " << loc.toJson() << "\n"
|
||||
<< in << "}";
|
||||
return ss.str();
|
||||
}
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// IdentifierNode — Tanımlayıcı Referansı
|
||||
// ============================================================================
|
||||
|
||||
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";
|
||||
}
|
||||
|
||||
std::string toJson(int depth = 0) override {
|
||||
std::string in = jsonIndent(depth);
|
||||
std::string name = parserToken.token ? parserToken.token->token : "?";
|
||||
std::ostringstream ss;
|
||||
ss << in << "{\n"
|
||||
<< in << " \"kind\": \"Identifier\",\n"
|
||||
<< in << " \"name\": \"" << jsonEscape(name) << "\",\n"
|
||||
<< in << " \"location\": " << loc.toJson() << "\n"
|
||||
<< in << "}";
|
||||
return ss.str();
|
||||
}
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// PostfixNode — Son Ek İşlem (a++, a--)
|
||||
// ============================================================================
|
||||
|
||||
class PostfixNode : public ASTNode {
|
||||
public:
|
||||
ASTNode* operand = nullptr;
|
||||
TokenType Operator;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
std::string toJson(int depth = 0) override {
|
||||
std::string in = jsonIndent(depth);
|
||||
std::string opSym = "?";
|
||||
auto it = OPERATOR_MAP_REV.find(Operator);
|
||||
if (it != OPERATOR_MAP_REV.end()) opSym = std::string(it->second);
|
||||
|
||||
std::ostringstream ss;
|
||||
ss << in << "{\n"
|
||||
<< in << " \"kind\": \"Postfix\",\n"
|
||||
<< in << " \"operator\": \"" << jsonEscape(opSym) << "\"";
|
||||
if (operand) {
|
||||
ss << ",\n" << in << " \"operand\":\n"
|
||||
<< operand->toJson(depth + 2);
|
||||
}
|
||||
ss << "\n" << in << "}";
|
||||
return ss.str();
|
||||
}
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// IfStatementNode — if / else
|
||||
// ============================================================================
|
||||
|
||||
class IfStatementNode : public ASTNode {
|
||||
public:
|
||||
ASTNode* condition = nullptr;
|
||||
ASTNode* thenBranch = nullptr;
|
||||
ASTNode* elseBranch = nullptr;
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
std::string toJson(int depth = 0) override {
|
||||
std::string in = jsonIndent(depth);
|
||||
std::ostringstream ss;
|
||||
ss << in << "{\n"
|
||||
<< in << " \"kind\": \"IfStatement\"";
|
||||
if (condition) {
|
||||
ss << ",\n" << in << " \"condition\":\n"
|
||||
<< condition->toJson(depth + 2);
|
||||
}
|
||||
if (thenBranch) {
|
||||
ss << ",\n" << in << " \"then\":\n"
|
||||
<< thenBranch->toJson(depth + 2);
|
||||
}
|
||||
if (elseBranch) {
|
||||
ss << ",\n" << in << " \"else\":\n"
|
||||
<< elseBranch->toJson(depth + 2);
|
||||
}
|
||||
ss << "\n" << in << "}";
|
||||
return ss.str();
|
||||
}
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// WhileStatementNode — while Döngüsü
|
||||
// ============================================================================
|
||||
|
||||
class WhileStatementNode : public ASTNode {
|
||||
public:
|
||||
ASTNode* condition = nullptr;
|
||||
ASTNode* body = nullptr;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
std::string toJson(int depth = 0) override {
|
||||
std::string in = jsonIndent(depth);
|
||||
std::ostringstream ss;
|
||||
ss << in << "{\n"
|
||||
<< in << " \"kind\": \"WhileStatement\"";
|
||||
if (condition) {
|
||||
ss << ",\n" << in << " \"condition\":\n"
|
||||
<< condition->toJson(depth + 2);
|
||||
}
|
||||
if (body) {
|
||||
ss << ",\n" << in << " \"body\":\n"
|
||||
<< body->toJson(depth + 2);
|
||||
}
|
||||
ss << "\n" << in << "}";
|
||||
return ss.str();
|
||||
}
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// ForStatementNode — for Döngüsü
|
||||
// ============================================================================
|
||||
|
||||
class ForStatementNode : public ASTNode {
|
||||
public:
|
||||
ASTNode* init = nullptr;
|
||||
ASTNode* condition = nullptr;
|
||||
ASTNode* update = nullptr;
|
||||
ASTNode* body = nullptr;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
std::string toJson(int depth = 0) override {
|
||||
std::string in = jsonIndent(depth);
|
||||
std::ostringstream ss;
|
||||
ss << in << "{\n"
|
||||
<< in << " \"kind\": \"ForStatement\"";
|
||||
if (init) {
|
||||
ss << ",\n" << in << " \"init\":\n"
|
||||
<< init->toJson(depth + 2);
|
||||
}
|
||||
if (condition) {
|
||||
ss << ",\n" << in << " \"condition\":\n"
|
||||
<< condition->toJson(depth + 2);
|
||||
}
|
||||
if (update) {
|
||||
ss << ",\n" << in << " \"update\":\n"
|
||||
<< update->toJson(depth + 2);
|
||||
}
|
||||
if (body) {
|
||||
ss << ",\n" << in << " \"body\":\n"
|
||||
<< body->toJson(depth + 2);
|
||||
}
|
||||
ss << "\n" << in << "}";
|
||||
return ss.str();
|
||||
}
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// DoWhileStatementNode — do-while Döngüsü
|
||||
// ============================================================================
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
std::string toJson(int depth = 0) override {
|
||||
std::string in = jsonIndent(depth);
|
||||
std::ostringstream ss;
|
||||
ss << in << "{\n"
|
||||
<< in << " \"kind\": \"DoWhileStatement\"";
|
||||
if (body) {
|
||||
ss << ",\n" << in << " \"body\":\n"
|
||||
<< body->toJson(depth + 2);
|
||||
}
|
||||
if (condition) {
|
||||
ss << ",\n" << in << " \"condition\":\n"
|
||||
<< condition->toJson(depth + 2);
|
||||
}
|
||||
ss << "\n" << in << "}";
|
||||
return ss.str();
|
||||
}
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// ReturnStatementNode — return [ifade]
|
||||
// ============================================================================
|
||||
|
||||
class ReturnStatementNode : public ASTNode {
|
||||
public:
|
||||
ASTNode* value = nullptr;
|
||||
|
||||
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";
|
||||
}
|
||||
}
|
||||
|
||||
std::string toJson(int depth = 0) override {
|
||||
std::string in = jsonIndent(depth);
|
||||
std::ostringstream ss;
|
||||
ss << in << "{\n"
|
||||
<< in << " \"kind\": \"ReturnStatement\"";
|
||||
if (value) {
|
||||
ss << ",\n" << in << " \"value\":\n"
|
||||
<< value->toJson(depth + 2);
|
||||
}
|
||||
ss << "\n" << in << "}";
|
||||
return ss.str();
|
||||
}
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// BreakStatementNode — break
|
||||
// ============================================================================
|
||||
|
||||
class BreakStatementNode : public ASTNode {
|
||||
public:
|
||||
BreakStatementNode() { kind = ASTKind::BreakStatement; }
|
||||
|
||||
void log(int indent = 0) override {
|
||||
std::cout << padRight("", indent) << "BreakStatement\n";
|
||||
}
|
||||
|
||||
std::string toJson(int depth = 0) override {
|
||||
std::string in = jsonIndent(depth);
|
||||
return in + "{\n" + in + " \"kind\": \"BreakStatement\"\n" + in + "}";
|
||||
}
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// ContinueStatementNode — continue
|
||||
// ============================================================================
|
||||
|
||||
class ContinueStatementNode : public ASTNode {
|
||||
public:
|
||||
ContinueStatementNode() { kind = ASTKind::ContinueStatement; }
|
||||
|
||||
void log(int indent = 0) override {
|
||||
std::cout << padRight("", indent) << "ContinueStatement\n";
|
||||
}
|
||||
|
||||
std::string toJson(int depth = 0) override {
|
||||
std::string in = jsonIndent(depth);
|
||||
return in + "{\n" + in + " \"kind\": \"ContinueStatement\"\n" + in + "}";
|
||||
}
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// ExpressionStatementNode — İfadeyi Statement Olarak Sarma
|
||||
// ============================================================================
|
||||
|
||||
class ExpressionStatementNode : public ASTNode {
|
||||
public:
|
||||
ASTNode* expression = nullptr;
|
||||
|
||||
ExpressionStatementNode() { kind = ASTKind::ExpressionStatement; }
|
||||
|
||||
void log(int indent = 0) override {
|
||||
std::cout << padRight("", indent) << "ExpressionStatement\n";
|
||||
if (expression) expression->log(indent + 2);
|
||||
}
|
||||
|
||||
std::string toJson(int depth = 0) override {
|
||||
std::string in = jsonIndent(depth);
|
||||
std::ostringstream ss;
|
||||
ss << in << "{\n"
|
||||
<< in << " \"kind\": \"ExpressionStatement\",\n"
|
||||
<< in << " \"location\": " << loc.toJson() << "";
|
||||
if (expression) {
|
||||
ss << ",\n" << in << " \"expression\":\n"
|
||||
<< expression->toJson(depth + 2);
|
||||
}
|
||||
ss << "\n" << in << "}";
|
||||
return ss.str();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// ============================================================================
|
||||
// CallExpressionNode — Fonksiyon Çağrısı f(a, b, ...)
|
||||
// ============================================================================
|
||||
|
||||
class CallExpressionNode : public ASTNode {
|
||||
public:
|
||||
ASTNode* callee = nullptr;
|
||||
std::vector<ASTNode*> arguments;
|
||||
|
||||
CallExpressionNode() { kind = ASTKind::Call; }
|
||||
|
||||
void log(int indent = 0) override {
|
||||
std::cout << padRight("", indent) << "Call\n";
|
||||
if (callee) {
|
||||
std::cout << padRight("", indent + 2) << "Callee:\n";
|
||||
callee->log(indent + 4);
|
||||
}
|
||||
std::cout << padRight("", indent + 2) << "Args (" << arguments.size() << "):\n";
|
||||
for (auto* a : arguments) a->log(indent + 4);
|
||||
}
|
||||
|
||||
std::string toJson(int depth = 0) override {
|
||||
std::string in = jsonIndent(depth);
|
||||
std::ostringstream ss;
|
||||
ss << in << "{\n"
|
||||
<< in << " \"kind\": \"Call\"";
|
||||
if (callee) {
|
||||
ss << ",\n" << in << " \"callee\":\n"
|
||||
<< callee->toJson(depth + 2);
|
||||
}
|
||||
ss << ",\n" << in << " \"arguments\": [\n";
|
||||
for (size_t i = 0; i < arguments.size(); i++) {
|
||||
ss << arguments[i]->toJson(depth + 3);
|
||||
if (i + 1 < arguments.size()) ss << ",";
|
||||
ss << "\n";
|
||||
}
|
||||
ss << in << " ]\n" << in << "}";
|
||||
return ss.str();
|
||||
}
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// MemberAccessNode — Üye Erişimi a.b veya a->b
|
||||
// ============================================================================
|
||||
|
||||
class MemberAccessNode : public ASTNode {
|
||||
public:
|
||||
ASTNode* object = nullptr;
|
||||
std::string member;
|
||||
bool arrow = false;
|
||||
|
||||
MemberAccessNode() { kind = ASTKind::MemberAccess; }
|
||||
|
||||
void log(int indent = 0) override {
|
||||
std::cout << padRight("", indent) << "MemberAccess "
|
||||
<< (arrow ? "->" : ".") << " " << member << "\n";
|
||||
if (object) object->log(indent + 2);
|
||||
}
|
||||
|
||||
std::string toJson(int depth = 0) override {
|
||||
std::string in = jsonIndent(depth);
|
||||
std::ostringstream ss;
|
||||
ss << in << "{\n"
|
||||
<< in << " \"kind\": \"MemberAccess\",\n"
|
||||
<< in << " \"member\": \"" << jsonEscape(member) << "\",\n"
|
||||
<< in << " \"arrow\": " << (arrow ? "true" : "false");
|
||||
if (object) {
|
||||
ss << ",\n" << in << " \"object\":\n"
|
||||
<< object->toJson(depth + 2);
|
||||
}
|
||||
ss << "\n" << in << "}";
|
||||
return ss.str();
|
||||
}
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// IndexExpressionNode — Dizi Erişimi a[i]
|
||||
// ============================================================================
|
||||
|
||||
class IndexExpressionNode : public ASTNode {
|
||||
public:
|
||||
ASTNode* object = nullptr;
|
||||
ASTNode* index = nullptr;
|
||||
|
||||
IndexExpressionNode() { kind = ASTKind::IndexExpression; }
|
||||
|
||||
void log(int indent = 0) override {
|
||||
std::cout << padRight("", indent) << "IndexExpression\n";
|
||||
if (object) {
|
||||
std::cout << padRight("", indent + 2) << "Object:\n";
|
||||
object->log(indent + 4);
|
||||
}
|
||||
if (index) {
|
||||
std::cout << padRight("", indent + 2) << "Index:\n";
|
||||
index->log(indent + 4);
|
||||
}
|
||||
}
|
||||
|
||||
std::string toJson(int depth = 0) override {
|
||||
std::string in = jsonIndent(depth);
|
||||
std::ostringstream ss;
|
||||
ss << in << "{\n"
|
||||
<< in << " \"kind\": \"IndexExpression\"";
|
||||
if (object) {
|
||||
ss << ",\n" << in << " \"object\":\n"
|
||||
<< object->toJson(depth + 2);
|
||||
}
|
||||
if (index) {
|
||||
ss << ",\n" << in << " \"index\":\n"
|
||||
<< index->toJson(depth + 2);
|
||||
}
|
||||
ss << "\n" << in << "}";
|
||||
return ss.str();
|
||||
}
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// StructDeclNode — struct Tanımı
|
||||
// ============================================================================
|
||||
|
||||
class StructDeclNode : public ASTNode {
|
||||
public:
|
||||
std::string name;
|
||||
|
||||
StructDeclNode() { kind = ASTKind::StructDecl; }
|
||||
|
||||
void log(int indent = 0) override {
|
||||
std::cout << padRight("", indent) << "StructDecl " << name << "\n";
|
||||
for (auto* c : getChildren()) c->log(indent + 2);
|
||||
}
|
||||
|
||||
std::string toJson(int depth = 0) override {
|
||||
std::string in = jsonIndent(depth);
|
||||
std::ostringstream ss;
|
||||
ss << in << "{\n"
|
||||
<< in << " \"kind\": \"StructDecl\",\n"
|
||||
<< in << " \"name\": \"" << jsonEscape(name) << "\",\n"
|
||||
<< in << " \"children\": [\n"
|
||||
<< childrenToJson(this, depth + 3)
|
||||
<< in << " ]\n"
|
||||
<< in << "}";
|
||||
return ss.str();
|
||||
}
|
||||
};
|
||||
#endif // SAQUT_AST
|
||||
|
|
|
|||
|
|
@ -1,143 +0,0 @@
|
|||
// ============================================================================
|
||||
// saQut Compiler — AST Deklarasyon Düğümleri
|
||||
// ============================================================================
|
||||
//
|
||||
// DİZİN: src/parser/ast_decl.hpp
|
||||
// İÇERİK: ProgramNode, FunctionDeclNode, VariableDeclNode, StructDeclNode
|
||||
//
|
||||
// ============================================================================
|
||||
|
||||
#ifndef SAQUT_AST_DECL
|
||||
#define SAQUT_AST_DECL
|
||||
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include "parser/ast_node.hpp"
|
||||
#include "parser/ast_json.hpp"
|
||||
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);
|
||||
}
|
||||
|
||||
std::string toJson(int depth = 0) override {
|
||||
std::string in = jsonIndent(depth);
|
||||
std::ostringstream ss;
|
||||
ss << in << "{\n"
|
||||
<< in << " \"kind\": \"Program\",\n"
|
||||
<< in << " \"children\": [\n"
|
||||
<< childrenToJson(this, depth + 3)
|
||||
<< in << " ]\n"
|
||||
<< in << "}";
|
||||
return ss.str();
|
||||
}
|
||||
};
|
||||
|
||||
class FunctionDeclNode : public ASTNode {
|
||||
public:
|
||||
std::string name;
|
||||
std::string returnType;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
std::string toJson(int depth = 0) override {
|
||||
std::string in = jsonIndent(depth);
|
||||
std::ostringstream ss;
|
||||
ss << in << "{\n"
|
||||
<< in << " \"kind\": \"FunctionDecl\",\n"
|
||||
<< in << " \"name\": \"" << jsonEscape(name) << "\",\n"
|
||||
<< in << " \"returnType\": \"" << jsonEscape(returnType) << "\",\n"
|
||||
<< in << " \"location\": " << loc.toJson() << ",\n"
|
||||
<< in << " \"children\": [\n"
|
||||
<< childrenToJson(this, depth + 3)
|
||||
<< in << " ]\n"
|
||||
<< in << "}";
|
||||
return ss.str();
|
||||
}
|
||||
};
|
||||
|
||||
class VariableDeclNode : public ASTNode {
|
||||
public:
|
||||
std::string varType;
|
||||
std::string name;
|
||||
ASTNode* initExpr = nullptr;
|
||||
|
||||
VariableDeclNode() { kind = ASTKind::VariableDecl; }
|
||||
|
||||
std::string toJson(int depth = 0) override {
|
||||
std::string in = jsonIndent(depth);
|
||||
std::ostringstream ss;
|
||||
ss << in << "{\n"
|
||||
<< in << " \"kind\": \"VariableDecl\",\n"
|
||||
<< in << " \"name\": \"" << jsonEscape(name) << "\",\n"
|
||||
<< in << " \"varType\": \"" << jsonEscape(varType) << "\",\n"
|
||||
<< in << " \"location\": " << loc.toJson() << "";
|
||||
if (initExpr) {
|
||||
ss << ",\n" << in << " \"initExpr\":\n"
|
||||
<< initExpr->toJson(depth + 2);
|
||||
}
|
||||
// Çoklu değişken bildirimindeki kardeşler (int a, b, c;)
|
||||
if (!getChildren().empty()) {
|
||||
ss << ",\n" << in << " \"declarators\": [\n";
|
||||
for (size_t i = 0; i < getChildren().size(); i++) {
|
||||
ss << ((VariableDeclNode*)getChildren()[i])->toJson(depth + 2);
|
||||
if (i + 1 < getChildren().size()) ss << ",";
|
||||
ss << "\n";
|
||||
}
|
||||
ss << in << " ]";
|
||||
}
|
||||
ss << "\n" << in << "}";
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
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";
|
||||
}
|
||||
// Kardeş değişkenleri de logla
|
||||
for (auto* child : getChildren()) {
|
||||
child->log(indent);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class StructDeclNode : public ASTNode {
|
||||
public:
|
||||
std::string name;
|
||||
|
||||
StructDeclNode() { kind = ASTKind::StructDecl; }
|
||||
|
||||
void log(int indent = 0) override {
|
||||
std::cout << padRight("", indent) << "StructDecl " << name << "\n";
|
||||
for (auto* c : getChildren()) c->log(indent + 2);
|
||||
}
|
||||
|
||||
std::string toJson(int depth = 0) override {
|
||||
std::string in = jsonIndent(depth);
|
||||
std::ostringstream ss;
|
||||
ss << in << "{\n"
|
||||
<< in << " \"kind\": \"StructDecl\",\n"
|
||||
<< in << " \"name\": \"" << jsonEscape(name) << "\",\n"
|
||||
<< in << " \"children\": [\n"
|
||||
<< childrenToJson(this, depth + 3)
|
||||
<< in << " ]\n"
|
||||
<< in << "}";
|
||||
return ss.str();
|
||||
}
|
||||
};
|
||||
#endif // SAQUT_AST_DECL
|
||||
|
|
@ -1,287 +0,0 @@
|
|||
// ============================================================================
|
||||
// saQut Compiler — AST İfade Düğümleri
|
||||
// ============================================================================
|
||||
//
|
||||
// DİZİN: src/parser/ast_expr.hpp
|
||||
// İÇERİK: BinaryExpr, Literal, Identifier, Postfix,
|
||||
// CallExpression, MemberAccess, IndexExpression
|
||||
//
|
||||
// ============================================================================
|
||||
|
||||
#ifndef SAQUT_AST_EXPR
|
||||
#define SAQUT_AST_EXPR
|
||||
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include "parser/ast_node.hpp"
|
||||
#include "parser/ast_json.hpp"
|
||||
class BinaryExpressionNode : public ASTNode {
|
||||
public:
|
||||
TokenType Operator;
|
||||
ASTNode* Left = nullptr;
|
||||
ASTNode* Right = nullptr;
|
||||
|
||||
BinaryExpressionNode() { kind = ASTKind::BinaryExpression; }
|
||||
|
||||
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::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";
|
||||
if (Right) Right->log(indent + 2);
|
||||
if (Left) Left->log(indent + 2);
|
||||
}
|
||||
|
||||
std::string toJson(int depth = 0) override {
|
||||
std::string in = jsonIndent(depth);
|
||||
std::string opSym = "?";
|
||||
auto it = OPERATOR_MAP_REV.find(Operator);
|
||||
if (it != OPERATOR_MAP_REV.end()) opSym = std::string(it->second);
|
||||
|
||||
std::ostringstream ss;
|
||||
ss << in << "{\n"
|
||||
<< in << " \"kind\": \"BinaryExpression\",\n"
|
||||
<< in << " \"operator\": \"" << jsonEscape(opSym) << "\",\n"
|
||||
<< in << " \"location\": " << loc.toJson() << "";
|
||||
if (Left) {
|
||||
ss << ",\n" << in << " \"left\":\n"
|
||||
<< Left->toJson(depth + 2);
|
||||
}
|
||||
if (Right) {
|
||||
ss << ",\n" << in << " \"right\":\n"
|
||||
<< Right->toJson(depth + 2);
|
||||
}
|
||||
ss << "\n" << in << "}";
|
||||
return ss.str();
|
||||
}
|
||||
};
|
||||
|
||||
// LiteralType enum'u ve literalTypeToString ast_node.hpp'de tanımlıdır.
|
||||
|
||||
class LiteralNode : public ASTNode {
|
||||
public:
|
||||
Token* lexerToken = nullptr;
|
||||
ParserToken parserToken;
|
||||
|
||||
LiteralType literalType = LiteralType::INTEGER;
|
||||
int literalBase = 10; // 10, 16, 8, 2 (sadece INTEGER/FLOAT için)
|
||||
bool isFloatValue = false; // Ondalıklı mı? (sadece INTEGER/FLOAT için)
|
||||
|
||||
LiteralNode() { kind = ASTKind::Literal; }
|
||||
|
||||
void log(int indent = 0) override {
|
||||
std::cout << padRight("", indent)
|
||||
<< "Literal {" << parserToken.token->token << "} "
|
||||
<< literalTypeToString(literalType);
|
||||
if (literalType == LiteralType::INTEGER && literalBase != 10)
|
||||
std::cout << " (base " << literalBase << ")";
|
||||
std::cout << "\n";
|
||||
}
|
||||
|
||||
std::string toJson(int depth = 0) override {
|
||||
std::string in = jsonIndent(depth);
|
||||
std::string val = parserToken.token ? parserToken.token->token : "?";
|
||||
std::ostringstream ss;
|
||||
ss << in << "{\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 << " \"location\": " << loc.toJson() << "\n"
|
||||
<< in << "}";
|
||||
return ss.str();
|
||||
}
|
||||
};
|
||||
|
||||
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";
|
||||
}
|
||||
|
||||
std::string toJson(int depth = 0) override {
|
||||
std::string in = jsonIndent(depth);
|
||||
std::string name = parserToken.token ? parserToken.token->token : "?";
|
||||
std::ostringstream ss;
|
||||
ss << in << "{\n"
|
||||
<< in << " \"kind\": \"Identifier\",\n"
|
||||
<< in << " \"name\": \"" << jsonEscape(name) << "\",\n"
|
||||
<< in << " \"location\": " << loc.toJson() << "\n"
|
||||
<< in << "}";
|
||||
return ss.str();
|
||||
}
|
||||
};
|
||||
|
||||
class PostfixNode : public ASTNode {
|
||||
public:
|
||||
ASTNode* operand = nullptr;
|
||||
TokenType Operator;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
std::string toJson(int depth = 0) override {
|
||||
std::string in = jsonIndent(depth);
|
||||
std::string opSym = "?";
|
||||
auto it = OPERATOR_MAP_REV.find(Operator);
|
||||
if (it != OPERATOR_MAP_REV.end()) opSym = std::string(it->second);
|
||||
|
||||
std::ostringstream ss;
|
||||
ss << in << "{\n"
|
||||
<< in << " \"kind\": \"Postfix\",\n"
|
||||
<< in << " \"operator\": \"" << jsonEscape(opSym) << "\"";
|
||||
if (operand) {
|
||||
ss << ",\n" << in << " \"operand\":\n"
|
||||
<< operand->toJson(depth + 2);
|
||||
}
|
||||
ss << "\n" << in << "}";
|
||||
return ss.str();
|
||||
}
|
||||
};
|
||||
|
||||
class CallExpressionNode : public ASTNode {
|
||||
public:
|
||||
ASTNode* callee = nullptr;
|
||||
std::vector<ASTNode*> arguments;
|
||||
|
||||
CallExpressionNode() { kind = ASTKind::Call; }
|
||||
|
||||
void log(int indent = 0) override {
|
||||
std::cout << padRight("", indent) << "Call\n";
|
||||
if (callee) {
|
||||
std::cout << padRight("", indent + 2) << "Callee:\n";
|
||||
callee->log(indent + 4);
|
||||
}
|
||||
std::cout << padRight("", indent + 2) << "Args (" << arguments.size() << "):\n";
|
||||
for (auto* a : arguments) a->log(indent + 4);
|
||||
}
|
||||
|
||||
std::string toJson(int depth = 0) override {
|
||||
std::string in = jsonIndent(depth);
|
||||
std::ostringstream ss;
|
||||
ss << in << "{\n"
|
||||
<< in << " \"kind\": \"Call\"";
|
||||
if (callee) {
|
||||
ss << ",\n" << in << " \"callee\":\n"
|
||||
<< callee->toJson(depth + 2);
|
||||
}
|
||||
ss << ",\n" << in << " \"arguments\": [\n";
|
||||
for (size_t i = 0; i < arguments.size(); i++) {
|
||||
ss << arguments[i]->toJson(depth + 3);
|
||||
if (i + 1 < arguments.size()) ss << ",";
|
||||
ss << "\n";
|
||||
}
|
||||
ss << in << " ]\n" << in << "}";
|
||||
return ss.str();
|
||||
}
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// MemberAccessNode — Üye Erişimi a.b veya a->b
|
||||
// ============================================================================
|
||||
|
||||
|
||||
class MemberAccessNode : public ASTNode {
|
||||
public:
|
||||
ASTNode* object = nullptr;
|
||||
std::string member;
|
||||
bool arrow = false;
|
||||
|
||||
MemberAccessNode() { kind = ASTKind::MemberAccess; }
|
||||
|
||||
void log(int indent = 0) override {
|
||||
std::cout << padRight("", indent) << "MemberAccess "
|
||||
<< (arrow ? "->" : ".") << " " << member << "\n";
|
||||
if (object) object->log(indent + 2);
|
||||
}
|
||||
|
||||
std::string toJson(int depth = 0) override {
|
||||
std::string in = jsonIndent(depth);
|
||||
std::ostringstream ss;
|
||||
ss << in << "{\n"
|
||||
<< in << " \"kind\": \"MemberAccess\",\n"
|
||||
<< in << " \"member\": \"" << jsonEscape(member) << "\",\n"
|
||||
<< in << " \"arrow\": " << (arrow ? "true" : "false");
|
||||
if (object) {
|
||||
ss << ",\n" << in << " \"object\":\n"
|
||||
<< object->toJson(depth + 2);
|
||||
}
|
||||
ss << "\n" << in << "}";
|
||||
return ss.str();
|
||||
}
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// IndexExpressionNode — Dizi Erişimi a[i]
|
||||
// ============================================================================
|
||||
|
||||
|
||||
class IndexExpressionNode : public ASTNode {
|
||||
public:
|
||||
ASTNode* object = nullptr;
|
||||
ASTNode* index = nullptr;
|
||||
|
||||
IndexExpressionNode() { kind = ASTKind::IndexExpression; }
|
||||
|
||||
void log(int indent = 0) override {
|
||||
std::cout << padRight("", indent) << "IndexExpression\n";
|
||||
if (object) {
|
||||
std::cout << padRight("", indent + 2) << "Object:\n";
|
||||
object->log(indent + 4);
|
||||
}
|
||||
if (index) {
|
||||
std::cout << padRight("", indent + 2) << "Index:\n";
|
||||
index->log(indent + 4);
|
||||
}
|
||||
}
|
||||
|
||||
std::string toJson(int depth = 0) override {
|
||||
std::string in = jsonIndent(depth);
|
||||
std::ostringstream ss;
|
||||
ss << in << "{\n"
|
||||
<< in << " \"kind\": \"IndexExpression\"";
|
||||
if (object) {
|
||||
ss << ",\n" << in << " \"object\":\n"
|
||||
<< object->toJson(depth + 2);
|
||||
}
|
||||
if (index) {
|
||||
ss << ",\n" << in << " \"index\":\n"
|
||||
<< index->toJson(depth + 2);
|
||||
}
|
||||
ss << "\n" << in << "}";
|
||||
return ss.str();
|
||||
}
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// StructDeclNode — struct Tanımı
|
||||
// ============================================================================
|
||||
|
||||
|
||||
#endif // SAQUT_AST_EXPR
|
||||
|
|
@ -1,134 +0,0 @@
|
|||
// ============================================================================
|
||||
// saQut Compiler — Temiz JSON Üretici (JsonObject)
|
||||
// ============================================================================
|
||||
//
|
||||
// DİZİN: src/parser/ast_json.hpp
|
||||
// KATMAN: AST — Sadece AST düğümlerinin toJson() metotları için
|
||||
// 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();
|
||||
//
|
||||
// ============================================================================
|
||||
|
||||
#ifndef SAQUT_AST_JSON
|
||||
#define SAQUT_AST_JSON
|
||||
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
|
||||
// Girinti sabiti (tools.hpp'deki jsonIndent ile uyumlu)
|
||||
#define JSON_INDENT 2
|
||||
|
||||
// jsonEscape ve jsonIndent tools.hpp'de tanımlıdır.
|
||||
|
||||
// ============================================================================
|
||||
// JsonObject — JSON Nesne Builder
|
||||
// ============================================================================
|
||||
//
|
||||
// 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();
|
||||
//
|
||||
// ============================================================================
|
||||
|
||||
class JsonObject {
|
||||
public:
|
||||
JsonObject(int depth)
|
||||
: m_indent(jsonIndent(depth)),
|
||||
m_indentInner(jsonIndent(depth + 1))
|
||||
{
|
||||
m_ss << m_indent << "{\n";
|
||||
}
|
||||
|
||||
// String alan ekle (değer tırnak içinde yazılır)
|
||||
void add(const std::string& key, const std::string& value) {
|
||||
addRaw(key, "\"" + jsonEscape(value) + "\"");
|
||||
}
|
||||
|
||||
// Sayısal alan ekle (değer olduğu gibi yazılır)
|
||||
void add(const std::string& key, int value) {
|
||||
addRaw(key, std::to_string(value));
|
||||
}
|
||||
|
||||
// Boolean alan ekle
|
||||
void add(const std::string& key, bool value) {
|
||||
addRaw(key, value ? "true" : "false");
|
||||
}
|
||||
|
||||
// Ham JSON değeri ekle (önceden formatlanmış, tırnaklanmamış)
|
||||
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;
|
||||
}
|
||||
|
||||
// Alt nesne ekle (bir alt seviyede JSON nesnesi)
|
||||
void addNested(const std::string& key, const std::string& nestedJson) {
|
||||
addRaw(key, nestedJson);
|
||||
}
|
||||
|
||||
// Koşullu string alan (value boş değilse ekle)
|
||||
void addIfNotEmpty(const std::string& key, const std::string& value) {
|
||||
if (!value.empty()) add(key, value);
|
||||
}
|
||||
|
||||
// Koşullu sayı alan (value varsayılandan farklıysa ekle)
|
||||
void addIfNot(const std::string& key, int value, int defaultValue) {
|
||||
if (value != defaultValue) add(key, value);
|
||||
}
|
||||
|
||||
// Dizi alanı (callback içinde addItem çağrılır)
|
||||
template<typename Fn>
|
||||
void addArray(const std::string& key, Fn callback) {
|
||||
if (m_hasFields) m_ss << ",\n";
|
||||
m_ss << m_indentInner << "\"" << jsonEscape(key) << "\": [\n";
|
||||
m_arrayDepth++;
|
||||
callback();
|
||||
m_arrayDepth--;
|
||||
m_ss << "\n" << m_indentInner << "]";
|
||||
m_hasFields = true;
|
||||
}
|
||||
|
||||
// Diziye eleman ekle (addArray callback'i içinde kullanılır)
|
||||
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;
|
||||
}
|
||||
|
||||
// Nesneyi kapat ve string olarak döndür
|
||||
std::string str() {
|
||||
m_ss << "\n" << m_indent << "}";
|
||||
return m_ss.str();
|
||||
}
|
||||
|
||||
private:
|
||||
std::ostringstream m_ss;
|
||||
std::string m_indent; // Bu nesnenin girintisi
|
||||
std::string m_indentInner; // Bir alt seviye girinti
|
||||
bool m_hasFields = false;
|
||||
int m_arrayDepth = 0; // İç içe dizi seviyesi
|
||||
bool m_hasArrayItem = false;
|
||||
};
|
||||
|
||||
#endif // SAQUT_AST_JSON
|
||||
|
|
@ -1,134 +0,0 @@
|
|||
// ============================================================================
|
||||
// saQut Compiler — AST Düğüm Tabanı (ASTNode, ASTKind, LiteralType)
|
||||
// ============================================================================
|
||||
//
|
||||
// DİZİN: src/parser/ast_node.hpp
|
||||
// KATMAN: Katman 3 — Parser
|
||||
// BAĞIMLI: core/location.hpp, parser/token.hpp, tools.hpp
|
||||
//
|
||||
// Bu dosya: ASTNode taban sınıfını, ASTKind enum'unu, LiteralType enum'unu
|
||||
// ve çocuk düğümleri JSON olarak yazdırmak için childrenToJson yardımcısını
|
||||
// içerir. Diğer tüm düğüm sınıfları bu dosyayı include eder.
|
||||
//
|
||||
// ============================================================================
|
||||
|
||||
#ifndef SAQUT_AST_NODE
|
||||
#define SAQUT_AST_NODE
|
||||
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "core/location.hpp"
|
||||
#include "parser/token.hpp"
|
||||
#include "tools.hpp"
|
||||
|
||||
// ============================================================================
|
||||
// ASTKind — Düğüm Tipi Enum
|
||||
// ============================================================================
|
||||
|
||||
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)
|
||||
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 + ;
|
||||
Call, // Fonksiyon çağrısı f(args)
|
||||
MemberAccess, // Üye erişimi a.b, a->b
|
||||
IndexExpression, // Dizi erişimi a[i]
|
||||
StructDecl, // struct tanımı
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// LiteralType — Sabit Değer Alt Tipleri
|
||||
// ============================================================================
|
||||
|
||||
enum class LiteralType : uint8_t {
|
||||
INTEGER, // Tamsayı (decimal, hex, octal, binary)
|
||||
FLOAT, // Ondalıklı sayı (3.14, 1e-5)
|
||||
STRING, // Metin ("hello")
|
||||
BOOLEAN, // true / false
|
||||
BOŞ // null
|
||||
};
|
||||
|
||||
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: Tipini bilir (ASTKind enum)
|
||||
// - parent: Ebeveynine işaret eder
|
||||
// - loc: Kaynak koddaki konumunu bilir
|
||||
// - log(): Konsola yazdırılabilir
|
||||
// - toJson: JSON olarak serileştirilebilir
|
||||
//
|
||||
// ============================================================================
|
||||
|
||||
class ASTNode {
|
||||
public:
|
||||
ASTKind kind;
|
||||
ASTNode* parent = nullptr;
|
||||
SourceLocation loc;
|
||||
|
||||
virtual void log(int indent = 0) {
|
||||
(void)indent;
|
||||
std::cout << "<Unknown>\n";
|
||||
}
|
||||
|
||||
virtual std::string toJson(int indent = 0) {
|
||||
(void)indent;
|
||||
return "{\"kind\":\"Unknown\"}";
|
||||
}
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// childrenToJson — Düğümün çocuklarını JSON array olarak yaz
|
||||
// ============================================================================
|
||||
|
||||
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 << ",";
|
||||
ss << "\n";
|
||||
}
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
#endif // SAQUT_AST_NODE
|
||||
|
|
@ -1,307 +0,0 @@
|
|||
// ============================================================================
|
||||
// saQut Compiler — AST Deyim Düğümleri
|
||||
// ============================================================================
|
||||
//
|
||||
// DİZİN: src/parser/ast_stmt.hpp
|
||||
// İÇERİK: Block, If, While, For, DoWhile,
|
||||
// Return, Break, Continue, ExpressionStatement
|
||||
//
|
||||
// ============================================================================
|
||||
|
||||
#ifndef SAQUT_AST_STMT
|
||||
#define SAQUT_AST_STMT
|
||||
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include "parser/ast_node.hpp"
|
||||
#include "parser/ast_json.hpp"
|
||||
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);
|
||||
}
|
||||
|
||||
std::string toJson(int depth = 0) override {
|
||||
std::string in = jsonIndent(depth);
|
||||
std::ostringstream ss;
|
||||
ss << in << "{\n"
|
||||
<< in << " \"kind\": \"Block\",\n"
|
||||
<< in << " \"children\": [\n"
|
||||
<< childrenToJson(this, depth + 3)
|
||||
<< in << " ]\n"
|
||||
<< in << "}";
|
||||
return ss.str();
|
||||
}
|
||||
};
|
||||
|
||||
class IfStatementNode : public ASTNode {
|
||||
public:
|
||||
ASTNode* condition = nullptr;
|
||||
ASTNode* thenBranch = nullptr;
|
||||
ASTNode* elseBranch = nullptr;
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
std::string toJson(int depth = 0) override {
|
||||
std::string in = jsonIndent(depth);
|
||||
std::ostringstream ss;
|
||||
ss << in << "{\n"
|
||||
<< in << " \"kind\": \"IfStatement\"";
|
||||
if (condition) {
|
||||
ss << ",\n" << in << " \"condition\":\n"
|
||||
<< condition->toJson(depth + 2);
|
||||
}
|
||||
if (thenBranch) {
|
||||
ss << ",\n" << in << " \"then\":\n"
|
||||
<< thenBranch->toJson(depth + 2);
|
||||
}
|
||||
if (elseBranch) {
|
||||
ss << ",\n" << in << " \"else\":\n"
|
||||
<< elseBranch->toJson(depth + 2);
|
||||
}
|
||||
ss << "\n" << in << "}";
|
||||
return ss.str();
|
||||
}
|
||||
};
|
||||
|
||||
class WhileStatementNode : public ASTNode {
|
||||
public:
|
||||
ASTNode* condition = nullptr;
|
||||
ASTNode* body = nullptr;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
std::string toJson(int depth = 0) override {
|
||||
std::string in = jsonIndent(depth);
|
||||
std::ostringstream ss;
|
||||
ss << in << "{\n"
|
||||
<< in << " \"kind\": \"WhileStatement\"";
|
||||
if (condition) {
|
||||
ss << ",\n" << in << " \"condition\":\n"
|
||||
<< condition->toJson(depth + 2);
|
||||
}
|
||||
if (body) {
|
||||
ss << ",\n" << in << " \"body\":\n"
|
||||
<< body->toJson(depth + 2);
|
||||
}
|
||||
ss << "\n" << in << "}";
|
||||
return ss.str();
|
||||
}
|
||||
};
|
||||
|
||||
class ForStatementNode : public ASTNode {
|
||||
public:
|
||||
ASTNode* init = nullptr;
|
||||
ASTNode* condition = nullptr;
|
||||
ASTNode* update = nullptr;
|
||||
ASTNode* body = nullptr;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
std::string toJson(int depth = 0) override {
|
||||
std::string in = jsonIndent(depth);
|
||||
std::ostringstream ss;
|
||||
ss << in << "{\n"
|
||||
<< in << " \"kind\": \"ForStatement\"";
|
||||
if (init) {
|
||||
ss << ",\n" << in << " \"init\":\n"
|
||||
<< init->toJson(depth + 2);
|
||||
}
|
||||
if (condition) {
|
||||
ss << ",\n" << in << " \"condition\":\n"
|
||||
<< condition->toJson(depth + 2);
|
||||
}
|
||||
if (update) {
|
||||
ss << ",\n" << in << " \"update\":\n"
|
||||
<< update->toJson(depth + 2);
|
||||
}
|
||||
if (body) {
|
||||
ss << ",\n" << in << " \"body\":\n"
|
||||
<< body->toJson(depth + 2);
|
||||
}
|
||||
ss << "\n" << in << "}";
|
||||
return ss.str();
|
||||
}
|
||||
};
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
std::string toJson(int depth = 0) override {
|
||||
std::string in = jsonIndent(depth);
|
||||
std::ostringstream ss;
|
||||
ss << in << "{\n"
|
||||
<< in << " \"kind\": \"DoWhileStatement\"";
|
||||
if (body) {
|
||||
ss << ",\n" << in << " \"body\":\n"
|
||||
<< body->toJson(depth + 2);
|
||||
}
|
||||
if (condition) {
|
||||
ss << ",\n" << in << " \"condition\":\n"
|
||||
<< condition->toJson(depth + 2);
|
||||
}
|
||||
ss << "\n" << in << "}";
|
||||
return ss.str();
|
||||
}
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// ReturnStatementNode — return [ifade]
|
||||
// ============================================================================
|
||||
|
||||
class ReturnStatementNode : public ASTNode {
|
||||
public:
|
||||
ASTNode* value = nullptr;
|
||||
|
||||
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";
|
||||
}
|
||||
}
|
||||
|
||||
std::string toJson(int depth = 0) override {
|
||||
std::string in = jsonIndent(depth);
|
||||
std::ostringstream ss;
|
||||
ss << in << "{\n"
|
||||
<< in << " \"kind\": \"ReturnStatement\"";
|
||||
if (value) {
|
||||
ss << ",\n" << in << " \"value\":\n"
|
||||
<< value->toJson(depth + 2);
|
||||
}
|
||||
ss << "\n" << in << "}";
|
||||
return ss.str();
|
||||
}
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// BreakStatementNode — break
|
||||
// ============================================================================
|
||||
|
||||
class BreakStatementNode : public ASTNode {
|
||||
public:
|
||||
BreakStatementNode() { kind = ASTKind::BreakStatement; }
|
||||
|
||||
void log(int indent = 0) override {
|
||||
std::cout << padRight("", indent) << "BreakStatement\n";
|
||||
}
|
||||
|
||||
std::string toJson(int depth = 0) override {
|
||||
std::string in = jsonIndent(depth);
|
||||
return in + "{\n" + in + " \"kind\": \"BreakStatement\"\n" + in + "}";
|
||||
}
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// ContinueStatementNode — continue
|
||||
// ============================================================================
|
||||
|
||||
class ContinueStatementNode : public ASTNode {
|
||||
public:
|
||||
ContinueStatementNode() { kind = ASTKind::ContinueStatement; }
|
||||
|
||||
void log(int indent = 0) override {
|
||||
std::cout << padRight("", indent) << "ContinueStatement\n";
|
||||
}
|
||||
|
||||
std::string toJson(int depth = 0) override {
|
||||
std::string in = jsonIndent(depth);
|
||||
return in + "{\n" + in + " \"kind\": \"ContinueStatement\"\n" + in + "}";
|
||||
}
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// ExpressionStatementNode — İfadeyi Statement Olarak Sarma
|
||||
// ============================================================================
|
||||
|
||||
class ExpressionStatementNode : public ASTNode {
|
||||
public:
|
||||
ASTNode* expression = nullptr;
|
||||
|
||||
ExpressionStatementNode() { kind = ASTKind::ExpressionStatement; }
|
||||
|
||||
void log(int indent = 0) override {
|
||||
std::cout << padRight("", indent) << "ExpressionStatement\n";
|
||||
if (expression) expression->log(indent + 2);
|
||||
}
|
||||
|
||||
std::string toJson(int depth = 0) override {
|
||||
std::string in = jsonIndent(depth);
|
||||
std::ostringstream ss;
|
||||
ss << in << "{\n"
|
||||
<< in << " \"kind\": \"ExpressionStatement\",\n"
|
||||
<< in << " \"location\": " << loc.toJson() << "";
|
||||
if (expression) {
|
||||
ss << ",\n" << in << " \"expression\":\n"
|
||||
<< expression->toJson(depth + 2);
|
||||
}
|
||||
ss << "\n" << in << "}";
|
||||
return ss.str();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// ============================================================================
|
||||
// CallExpressionNode — Fonksiyon Çağrısı f(a, b, ...)
|
||||
// ============================================================================
|
||||
|
||||
#endif // SAQUT_AST_STMT
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -1,62 +0,0 @@
|
|||
// ============================================================================
|
||||
// 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,409 +0,0 @@
|
|||
// ============================================================================
|
||||
// saQut Compiler — Parser Çekirdek (Program + İfadeler)
|
||||
// ============================================================================
|
||||
//
|
||||
// DİZİN: src/parser/parser_core.hpp
|
||||
// İÇERİK: Token navigasyonu, parse(), parseProgram(),
|
||||
// parseDeclaration(), parseExpression() [Pratt]
|
||||
//
|
||||
// ============================================================================
|
||||
|
||||
#ifndef SAQUT_PARSER_CORE
|
||||
#define SAQUT_PARSER_CORE
|
||||
|
||||
#include <iostream>
|
||||
#include "parser/parser_base.hpp"
|
||||
// --------------------------------------------------------------------------
|
||||
// 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();
|
||||
}
|
||||
|
||||
// struct
|
||||
if (ct.type == TokenType::KW_STRUCT)
|
||||
return parseStructDecl();
|
||||
|
||||
// Tip keyword'ü değil → statement
|
||||
return parseStatement();
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// parseFunctionDecl: Fonksiyon tanımı.
|
||||
|
||||
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->loc = ct.token ? ct.token->loc : SourceLocation{};
|
||||
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->loc = ct.token ? ct.token->loc : SourceLocation{};
|
||||
lit->lexerToken = ct.token;
|
||||
lit->parserToken = ct;
|
||||
// NumberToken'a cast edip base/isFloat bilgisini al
|
||||
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;
|
||||
}
|
||||
|
||||
// --- String literal: "hello" ---
|
||||
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;
|
||||
}
|
||||
|
||||
// --- Boolean/null literal: true, false, null ---
|
||||
if (ct.is({TokenType::KW_TRUE, TokenType::KW_FALSE, TokenType::KW_NULL})) {
|
||||
nextToken();
|
||||
LiteralNode* lit = new LiteralNode();
|
||||
// Token içeriğine göre boolean/null ayrımı
|
||||
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;
|
||||
}
|
||||
|
||||
// --- Identifier: x, myVar ---
|
||||
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;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// 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-- ---
|
||||
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;
|
||||
}
|
||||
|
||||
// --- Fonksiyon cagrisi: expr(args) ---
|
||||
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;
|
||||
}
|
||||
|
||||
// --- Dizi erisimi: expr[index] ---
|
||||
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;
|
||||
}
|
||||
|
||||
// --- Uye erisimi: expr.member / expr->member ---
|
||||
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;
|
||||
}
|
||||
|
||||
// --- Binary infix: expr OP expr ---
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
#endif // SAQUT_PARSER_CORE
|
||||
|
|
@ -1,159 +0,0 @@
|
|||
// ============================================================================
|
||||
// saQut Compiler — Parser Deklarasyonlar
|
||||
// ============================================================================
|
||||
//
|
||||
// DİZİN: src/parser/parser_decl.hpp
|
||||
// İÇERİK: parseFunctionDecl(), parseStructDecl(), parseVariableDecl()
|
||||
//
|
||||
// ============================================================================
|
||||
|
||||
#ifndef SAQUT_PARSER_DECL
|
||||
#define SAQUT_PARSER_DECL
|
||||
|
||||
#include <iostream>
|
||||
#include "parser/parser_base.hpp"
|
||||
inline ASTNode* Parser::parseFunctionDecl() {
|
||||
FunctionDeclNode* fn = new FunctionDeclNode();
|
||||
fn->loc = currentToken().token->loc;
|
||||
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;
|
||||
}
|
||||
// --------------------------------------------------------------------------
|
||||
// parseStructDecl: struct tanimi.
|
||||
// --------------------------------------------------------------------------
|
||||
inline 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;
|
||||
}
|
||||
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// parseVariableDecl: Değişken tanımı.
|
||||
//
|
||||
// Sözdizimi: Type Identifier [= Expression] {, Identifier [= Expression]} ;
|
||||
// Örnek: int x = 10;
|
||||
// float y; (initExpr = nullptr)
|
||||
// int first = 0, second = 1, next;
|
||||
//
|
||||
// Çoklu değişken:
|
||||
// İlk değişken ana düğüm olur. Virgülle ayrılmış ek değişkenler
|
||||
// ana düğümün children vektörüne eklenir. JSON çıktısında "declarators"
|
||||
// dizisi olarak görünür.
|
||||
// --------------------------------------------------------------------------
|
||||
inline ASTNode* Parser::parseVariableDecl() {
|
||||
// --- Tip ve ilk değişken adı ---
|
||||
VariableDeclNode* vd = new VariableDeclNode();
|
||||
vd->loc = currentToken().token->loc;
|
||||
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;
|
||||
}
|
||||
|
||||
vd->name = currentToken().token->token;
|
||||
nextToken(); // İsmi tüket
|
||||
|
||||
// Opsiyonel array boyutu: [expr]
|
||||
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(); // ']'
|
||||
}
|
||||
|
||||
// İlk değişkenin başlangıç değeri
|
||||
if (currentToken().type == TokenType::EQUAL) {
|
||||
nextToken(); // '=' tüket
|
||||
vd->initExpr = parseExpression();
|
||||
}
|
||||
|
||||
// --- Çoklu değişken: , identifier [= expr] ---
|
||||
while (currentToken().type == TokenType::COMMA) {
|
||||
nextToken(); // ',' tüket
|
||||
|
||||
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; // Aynı tip
|
||||
sibling->name = currentToken().token->token;
|
||||
nextToken(); // İsmi tüket
|
||||
|
||||
// Opsiyonel array boyutu: [expr]
|
||||
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(); // ']'
|
||||
}
|
||||
|
||||
// Başlangıç değeri
|
||||
if (currentToken().type == TokenType::EQUAL) {
|
||||
nextToken(); // '=' tüket
|
||||
sibling->initExpr = parseExpression();
|
||||
}
|
||||
|
||||
// Kardeş düğümü ana düğüme ekle
|
||||
vd->addChild(sibling);
|
||||
}
|
||||
|
||||
// Noktalı virgül (opsiyonel — parser hoşgörülü)
|
||||
if (currentToken().type == TokenType::SEMICOLON)
|
||||
nextToken();
|
||||
|
||||
return vd;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
|
||||
#endif // SAQUT_PARSER_DECL
|
||||
|
|
@ -1,322 +0,0 @@
|
|||
// ============================================================================
|
||||
// saQut Compiler — Parser Deyimler
|
||||
// ============================================================================
|
||||
//
|
||||
// DİZİN: src/parser/parser_stmt.hpp
|
||||
// İÇERİK: parseStatement(), parseBlock(), parseIf/While/For/DoWhile,
|
||||
// parseReturn/Break/Continue/ExpressionStatement
|
||||
//
|
||||
// ============================================================================
|
||||
|
||||
#ifndef SAQUT_PARSER_STMT
|
||||
#define SAQUT_PARSER_STMT
|
||||
|
||||
#include <iostream>
|
||||
#include "parser/parser_base.hpp"
|
||||
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();
|
||||
}
|
||||
|
||||
// struct tanımı: struct Name { ... }
|
||||
if (ct.type == TokenType::KW_STRUCT)
|
||||
return parseStructDecl();
|
||||
|
||||
// Hiçbiri değilse → ifade statement'ı (atama, fonksiyon çağrısı, ...)
|
||||
return parseExpressionStatement();
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// parseBlock: { statement* }
|
||||
// --------------------------------------------------------------------------
|
||||
inline ASTNode* Parser::parseBlock() {
|
||||
BlockNode* block = new BlockNode();
|
||||
block->loc = currentToken().token ? currentToken().token->loc : SourceLocation{};
|
||||
|
||||
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();
|
||||
ifNode->loc = currentToken().token->loc;
|
||||
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();
|
||||
ws->loc = currentToken().token->loc;
|
||||
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();
|
||||
fs->loc = currentToken().token->loc;
|
||||
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();
|
||||
dw->loc = currentToken().token->loc;
|
||||
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();
|
||||
rs->loc = currentToken().token->loc;
|
||||
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();
|
||||
bs->loc = currentToken().token->loc;
|
||||
nextToken(); // 'break' tüket
|
||||
if (currentToken().type == TokenType::SEMICOLON)
|
||||
nextToken();
|
||||
return bs;
|
||||
}
|
||||
|
||||
inline ASTNode* Parser::parseContinueStatement() {
|
||||
ContinueStatementNode* cs = new ContinueStatementNode();
|
||||
cs->loc = currentToken().token->loc;
|
||||
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->loc = currentToken().token ? currentToken().token->loc : SourceLocation{};
|
||||
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)
|
||||
|
||||
#endif // SAQUT_PARSER_STMT
|
||||
Loading…
Reference in New Issue