IR instruction seti (14 opcode): LOAD_CONST, LOAD_SLOT, ADD/SUB/MUL/DIV/MOD, LESS/LEQ/GT/GEQ/EQ/NEQ, JMP, JIF_FALSE, CALL, RETURN, CALLHOST IRGenerator (AST → IR): - Slot tabanlı register: parametreler slot 0..n, lokaller/geçiciler sonrası - freshSlot() monoton sayaç — slot asla geri verilmez - Backpatch: ileri-jump için emitJumpIfFalse(-1) → patchJump(idx) - Geri-jump: loopStart = currentInstrIndex() → emitJumpUnconditional(loopStart) - Builtin tespiti: resolvedSymbol->isBuiltin → CALLHOST Interpreter (bytecode VM): - Her iterasyonda callStack.back() taze alınır (referans güvenliği) - CALL: yeni frame + argüman kopyası + continue - RETURN: değer caller slotuna, frame pop + continue - ip CALL/RETURN'den önce ilerler — caller doğru noktadan devam eder - DIV: sıfıra bölme → runtime_error Doğrulama: build/saqut run file:examples/fibonacci.sqt → 55 / 55 ✓ tests/run.sh → TUM TESTLER GECTI ✓ |
||
|---|---|---|
| build | ||
| docs | ||
| examples | ||
| scripts | ||
| src | ||
| tests | ||
| .gitignore | ||
| CLAUDE.md | ||
| CMakeLists.txt | ||
| LICENSE.md | ||
| MISSION-FAZ3.md | ||
| readme.md | ||
readme.md
saQut
Programlanabilir, incelenebilir bir derleyici — bir "alet çantası" (toolbox).
saQut'un asıl varlık sebebi dilin kendisinden çok, derleme sürecinin her aşamasının dışarıdan görülebilir ve müdahale edilebilir olmasıdır. Token'lar, AST, sembol tablosu, optimizasyonun öncesi/sonrası ve IR — hepsi ayrı ayrı incelenebilir. Dil, bu aletin üzerinde çalıştığı küçük, prosedürel bir örnektir; vitrin değil, alet.
Uygulama dili C++'tır (header-only eğilimli, bkz. docs/fikirler.md ADR-003).
Şu an ne çalışıyor, ne çalışmıyor
Belgeler planlanan ile yapılanı net ayırır. Bugünkü gerçek durum:
✅ Çalışıyor (built)
- Lexer — karakter seviyesi tarama, konum takibi.
- Tokenizer — token üretimi (6 token tipi), yorum satırı desteği.
- Pratt parser — ifade (Pratt) + statement (recursive descent) ayrıştırma.
- AST — fonksiyon, blok, değişken tanımı, if/for/while/do-while/return, ifade node'ları.
- AST'nin JSON serileştirmesi —
saqut astile incelenebilir. - CLI komut yapısı —
tokens,ast,symbols,runiskeletleri. - Kaynak konum takibi (SourceLocation) — offset → (satır, sütun).
- Minimal IR deneyi — basit aritmetiği düşürür (örn.
1 + (7/3)→ kısa doğrusal komut dizisi). Henüz tam bir backend değil, bir deneydir.
🚧 Henüz yok (planned)
- Sembol tablosu
- Semantik analiz
- Tip sistemi
- Diagnostic (hata raporlama) motoru
- Optimizasyon
- IR + bytecode VM ile çalıştırma
feature/frontend-analysisdalı şu an yalnızca bu yapılmamış işin tasarım belgelerini içerir, kodunu değil.
Birinci kilometre taşı ("bitti" tanımı): derleyici fibonacci'yi
(recursive + iterative) ve basit matematik/döngü programlarını derleyip
çalıştırabilmeli. Referans program: examples/fibonacci.sqt.
Dil kimliği (kilitli)
Prosedürel, C ailesi sözdizimi, value semantics. İlk ifade doğrudan bir
işlem/tanım olabilir; zorunlu class/main boilerplate'i yoktur (Java'nın aksine).
| Özellik | Karar |
|---|---|
| Class / OOP / kalıtım | Yok |
| Closure | Yok |
| Generic | Yok |
Kullanıcıya açık pointer (* / &) |
Yok — derleyici/runtime içeride pointer'ı serbestçe kullanır |
struct |
Var |
| Tipli fonksiyonlar (dönüş + parametre) | Var |
Array (int[]) |
Var |
interface |
Ertelendi (v0 değil — gerekçe ADR-018) |
auto / tip çıkarımı |
Yok |
| Gizli int↔float dönüşümü | Yok (tek istisna: sabit folding) |
Gerekçe: prosedürel tasarım semantik karmaşıklığı en aza indirir ve hedeflerle
(fibonacci, matematik, sıralama, ayrıştırma) örtüşür. Standart C'de class
yoktur (o C++'tır); C, struct + fonksiyonun yettiğini kanıtlar.
Çalıştırma modeli (kilitli): IR + bytecode VM
saQut, kendi IR'sine derler ve bu IR'yi bir yorumlayıcı döngü (bytecode VM) ile çalıştırır.
- Tree-walker DEĞİL (çok yavaş).
- Gerçek makine-kodu JIT DEĞİL. Makine kodu üretimi (register allocation,
ABI/çağırma sözleşmeleri, çalıştırılabilir
mmapbellek) kapsam dışıdır — tek faydası ham hızdır ve hız burada öncelik değildir. Öncelikler determinizm ve incelenebilirliktir; bytecode VM ikisini de doğrudan sağlar. - Bellek bu modelde kolaydır: host (C++) heap'i kullanılır; v0 için özel runtime allocator gerekmez.
- C'ye transpile, geçerli bir İKİNCİ backend olarak ileride kalır (frontend backend-bağımsızdır — middle-end ayrımının amacı budur, ADR-006). İleride makine kodu istenirse elle code generator yazmak yerine libgccjit / LLVM'e bağlanılır — ama bu çok uzak gelecektir.
Eski belge/konuşmalarda geçen "JIT" terimi yanlış yönlendiricidir; doğru çerçeve IR + VM'dir.
Tasarım felsefesi — neden saQut farklı?
saQut "daha iyi bir dil" iddiasında değil. Farkı, derleyiciyi bir platform olarak ele almasında. İki taahhüt üstüne kuruludur:
1. Cam (gör + sorgula + içine müdahale et). Her aşama — token, AST, sembol
tablosu, tip, IR — kararlı, makine-okur ve çift yönlü bir arayüzden
erişilebilir olmalıdır. Sadece "dök ve göster" değil: kendi AST'ini verip tip
kontrolü isteyebilmeli, IR verip çalıştırabilmelisin. Turnusol testi: bir
yabancı, yalnızca saqut ast + saqut symbols çıktısından, bizden habersiz bir
LSP yazabiliyor mu? Cevap "evet" ise platform gerçektir.
2. Kafes (deterministik + yetenek-güvenli çalıştırma). Pointer yok, value semantics, scope-tabanlı bellek ve tek dış-dünya kapısı olan FFI seam (ADR-016) sayesinde saQut kodu, host'un açıkça izin verdiği fonksiyonlar dışında dünyaya dokunamaz. Bytecode VM deterministiktir (ADR-015): aynı girdi → aynı çıktı → aynı çalışma. "Sadelik" diye tasarlanan bu kararlar aslında bir yetenek-güvenliği (capability sandbox) kurar — güvenilmeyen veya AI-üretimi kodu güvenle çalıştırmak için biçilmiş kaftan.
Bu ikisinin ödülü — kayıt & tekrar (record-replay). 🚧 (vizyon, v0 değil; bkz. issue #94.) Belirsizliğin tek kaynağı (kullanıcı girdisi, zaman, IO, GC/thread kararları) FFI kapısından geçtiği için, mükemmel tekrar oynatma için her değişkeni her adımda kaydetmek gerekmez — yalnızca kapıdan geçen değerler kaydedilir, gerisi VM deterministik olarak yeniden çalıştırılarak üretilir. Boyut gigabayttan kilobayta düşer. Replay modunda FFI çağrıları gerçekten çalışmaz, kaydedilmiş değeri döndürür (dosyayı tekrar silmez, sunucuya tekrar istek atmaz). Böylece "benim makinemde çalışıyor, müşteride patlıyor" sorunu: müşteri bir dump yollar, sen çöküşü adım adım, aynı verilerle geri sararsın.
Log, önceden sormayı akıl ettiğin sorulara cevap verir; tekrar-oynatma, çöküşten sonra aklına gelen sorulara. Zaman-yolculuğu hata ayıklama ayrı bir altyapı değildir — cam sorgularına bir zaman koordinatı eklemektir.
⚠️ Bu ödülün bedeli v0'da ödenir: determinizm kutsaldır ve her belirsizlik kaynağı kayıt-altına-alınabilir tek kapıdan geçmelidir. Sonradan eklenemez; baştan korunur.
Mimari hatlar
KAYNAK KOD
│ lexer
▼
TOKEN'LAR ────────────── saqut tokens
│ parser (Pratt + recursive descent)
▼
AST ──────────────────── saqut ast
│ sembol toplama (iki geçişli) ┐
▼ │
SEMBOL TABLOSU ───────── saqut symbols │ FRONTEND
│ semantik analiz (annotation) │ (yapı + anlam)
▼ │
ANNOTATE EDİLMİŞ AST ─── saqut ast ┘
│ optimizasyon (opsiyonel, klon üstünde) ── MIDDLE-END
▼
IR ───────────────────── (planlanan) ┐
│ bytecode VM / yorumlayıcı döngü │ BACKEND
▼ │ (çalıştırma + FFI seam)
ÇALIŞTIRMA / ÇIKTI ───── saqut run ┘
- Frontend yapıyı ve anlamı modeller (tip, scope, dataflow).
- "Hangi çekirdek, hangi cihaz, ne zaman, hangi çıktı formatı" runtime/backend meselesidir — frontend'e yüklenmez.
CLI (mevcut + planlanan)
# --- çalışıyor ---
saqut tokens file:kaynak.sqt # token listesi
saqut ast file:kaynak.sqt # AST (JSON)
saqut symbols file:kaynak.sqt # sembol tablosu (iskelet)
# --- planlanan ---
saqut run file:kaynak.sqt # IR üret + bytecode VM ile çalıştır
saqut ast file:kaynak.sqt --optimized # klon, optimize edilmiş AST (öncesi/sonrası)
saqut transpile file:kaynak.sqt -o prog.c # ikinci backend (ileride)
Tasarım gereği her aşamanın çıktısı erken bir noktada dosyalanabilir/loglanabilir (programlanabilir derleyici). Token, ham AST, optimize AST ve IR ayrı ayrı kaydedilebilir.
Batteries / stdlib — kuzey yıldızı, ertelendi
Gerçek bir genel sürüm pil ile gelmeli (sıralama, sıkıştırma, kripto, JSON/XML/HTML ayrıştırma). Ama bu bugünün işi ve v0.1 değildir.
Mimari çerçeve (monolit korkusunu önler): derleyici pilleri çekirdeğine
gömmez. Bunun yerine küçük bir gerçek builtin kümesi (print, temel
zorunlular) + gerisi kütüphane/FFI ile gelir. "Batteries" sorunu aslında
bir sınır (FFI/link seam) sorunudur, "zlib'i yeniden yaz" sorunu değil.
Sınır bir kez çizilir, piller üstünde sonsuza dek birikir.
- JSON/XML/HTML ayrıştırıcıları saQut'un kendisinde yazılabilir (string + struct + fonksiyon + kontrol akışı yeter). İlk gerçek demo programları.
- Sıkıştırma/kripto: denenmiş C kütüphanelerine FFI ile bağlan. Kripto asla elle yazılmaz.
- Bugüne tek yansıması: IR/runtime tasarlanırken bilinçli bir FFI seam
("host fonksiyonu çağır" deliği) bırakılır.
printbunu zaten zorlar — bunu kaza değil, kasıtlı bir mekanizma yapıyoruz (ADR-016).
Belge haritası
| Belge | İçerik |
|---|---|
docs/fikirler.md |
ADR-001…005: backend stratejisi, parser, header-only, token, IR |
docs/adr-frontend-analiz.md |
ADR-006…019: frontend, analiz/optimizasyon, çalıştırma modeli, FFI, interface, bellek |
docs/roadmap-frontend.md |
Faz-faz uygulama planı (sembol tablosu → fibonacci) |
docs/transkript-frontend-tasarim.md |
Tasarım oturumunun transkripti |
examples/fibonacci.sqt |
Geçerli referans program (semantik + kod üretimi fixture'ı) |
examples/parser-stress/ |
Yalnızca parser'ı zorlayan, geçerli olmayan fixture'lar |
İlke
Bir şey çalışmadan önce çerçeve inşa etmekten kaçın. Önce uçtan uca tek bir
dikey dilim çalıştır (kaynak → IR → çalıştır; tamsayı aritmetiği + değişken +
kontrol akışı + tek bir print). Modülerlik bir kuzey yıldızıdır, v0.1
gereksinimi değil; ihtiyaç doğmadan eklenen her soyutlama daha az değil, daha
çok karmaşıklıktır.