feat: ADR-024/025/021 — string concat, try/catch/throw, nullable akış-analizi #115
47
CLAUDE.md
47
CLAUDE.md
|
|
@ -20,18 +20,46 @@ git'te izlenir.
|
||||||
ham hız değil). C'ye transpile ileride geçerli 2. backend. İleride makine kodu
|
ham hız değil). C'ye transpile ileride geçerli 2. backend. İleride makine kodu
|
||||||
gerekirse libgccjit/LLVM'e bağlanılır (çok uzak). Bellek = host C++ heap; özel
|
gerekirse libgccjit/LLVM'e bağlanılır (çok uzak). Bellek = host C++ heap; özel
|
||||||
allocator yok. (ADR-015)
|
allocator yok. (ADR-015)
|
||||||
- **Dil kimliği:** prosedürel, C-ailesi sözdizimi, value semantics, zorunlu
|
- **Dil kimliği:** prosedürel, C-ailesi sözdizimi, zorunlu class/main boilerplate
|
||||||
class/main boilerplate yok. **Yok:** class/OOP, closure, generic, kullanıcı
|
yok. **Semantik (ADR-020):** primitive (`int`/`float`/`bool`) = **değer**;
|
||||||
pointer'ı (`*`/`&`), auto/tip çıkarımı, gizli int↔float (tek istisna sabit
|
bileşik (`struct`/`array`/`string`) = **referans** (JS/Java/C# modeli). "Pointer
|
||||||
folding). **Var:** struct, tipli fonksiyonlar, array (`int[]`). `interface`
|
yok" = kullanıcıya `&`/`*` **sözdizimi** verilmez; derleyici/runtime içeride ve
|
||||||
**ertelendi** (reddedilmedi, ADR-018).
|
çalışma zamanında referansı sonuna kadar kullanır. **Yok:** OOP, closure,
|
||||||
|
generic, auto/tip çıkarımı, gizli int↔float (tek istisna sabit folding). **Var:**
|
||||||
|
struct, tipli fonksiyonlar, array (`int[]`). `class`/`function` sözdizimsel
|
||||||
|
**rezerve** (semantik ileride). `interface` **ertelendi** (ADR-018).
|
||||||
|
⚠️ Referans semantiği döngüsel-referans **sızıntısını** açtı → GC/döngü
|
||||||
|
toplayıcı borcu (**#56**, `karar-gerekli`).
|
||||||
|
- **Null güvenliği (ADR-021):** varsayılan non-null; nullable açıkça `Type?`
|
||||||
|
(Kotlin/Swift modeli). `null` yalnızca `T?`'ye atanır; `T?` üstünde doğrudan
|
||||||
|
erişim derleme hatası. **Akış-duyarlı null analizi** (`if (a != null)` daraltır;
|
||||||
|
guard/early-return; `&&` sağ tarafı). Runtime maliyeti **sıfır** (compile-time).
|
||||||
|
`a!` = runtime-kontrollü non-null iddiası. İlk gerçek akış-duyarlı analiz →
|
||||||
|
yapısal akış yeter, SSA gerekmez (#2 için veri).
|
||||||
|
- **Bellek/GC (ADR-022):** **basit, taşımasız, stop-the-world, deterministik
|
||||||
|
mark-sweep** (döngüleri toplar, "cage" korunur). **`shared_ptr`'ı kalıcı model
|
||||||
|
YAPMA** (refcount döngüde sızar = topuğa-sıkma). Kural: nesne modelini **baştan
|
||||||
|
GC-hazır** kur (header: tip+mark biti+liste; VM kök sayar; nesne çocuk
|
||||||
|
referansları sayar). v1: toplamasız (arena); v2: aynı model üstünde mark-sweep.
|
||||||
|
Asıl perf-katili GC kararı → basit tutarak de-risk edildi.
|
||||||
|
- **Eşitlik (ADR-023):** `==` primitive'de değer, referans (struct/array) **kimlik**
|
||||||
|
(aynı nesne). Derin/yapısal eşitlik **asla** `==`'e bağlanmaz → ayrı görünür
|
||||||
|
`deepEquals()` (PHP `==`/`===`/`clone` ailesi gibi). **String istisnası:** `==`
|
||||||
|
**içerik** olmalı (Java gotcha'sından kaçın) → string'i immutable değer-tipi
|
||||||
|
modelle (#40). `obj==obj`'i hata yapmak + kullanıcı-tanımlı eşitlik = uzak gelecek.
|
||||||
|
- **String (ADR-024):** **immutable değer-tipi, iç temsil UTF-8.** `s = s + "x"`
|
||||||
|
yeni string üretir; `==` içerik (ADR-023). Bayt/scalar/grapheme erişimi açıkça
|
||||||
|
ayrı (sahte O(1) karakter indeksi YOK). Verimli birleştirme için ileride builder.
|
||||||
|
Çözdüğü: #40 (yüzey), #9 (iç temsil). Mevcut `Value` string'i inline tutuyor —
|
||||||
|
immutable olduğu için bu yeterli; heap/object-model'e taşımak zorunlu değil.
|
||||||
- **Analiz vs Optimizasyon:** Analiz orijinal AST üstünde annotation; optimizasyon
|
- **Analiz vs Optimizasyon:** Analiz orijinal AST üstünde annotation; optimizasyon
|
||||||
**klon** üstünde dönüşüm. `ASTNode::clone()` yük taşıyan merkezi bileşen
|
**klon** üstünde dönüşüm. `ASTNode::clone()` yük taşıyan merkezi bileşen
|
||||||
(parent pointer'lar + sembol tablosu remap edilir, ADR-007). Fixpoint döngüsü +
|
(parent pointer'lar + sembol tablosu remap edilir, ADR-007). Fixpoint döngüsü +
|
||||||
iterasyon tavanı (`maxFixpointRounds`, ADR-009).
|
iterasyon tavanı (`maxFixpointRounds`, ADR-009).
|
||||||
- **Literal/tip kuralı:** tamsayı literali bağlama-göre tiplenir (`float x = 1;`
|
- **Literal/tip kuralı:** tamsayı literali bağlama-göre tiplenir (`float x = 1;`
|
||||||
geçerli; `int y = 1.5;`→E003; değişken→değişken gizli dönüşüm yok). Döngüsel
|
geçerli; `int y = 1.5;`→E003; değişken→değişken gizli dönüşüm yok). Döngüsel
|
||||||
by-value struct → E010. (ADR-010/011)
|
by-value struct → E010 (⚠️ ADR-020 ile revize: referansla tutulan struct alanı
|
||||||
|
artık döngü kurabilir, `Node next` meşru). (ADR-010/011)
|
||||||
- **FFI seam:** kasıtlı "host fonksiyonu çağır" mekanizması (`callhost`); `print`
|
- **FFI seam:** kasıtlı "host fonksiyonu çağır" mekanizması (`callhost`); `print`
|
||||||
ilk müşteri (ADR-016). Batteries = sınır/FFI problemi, "zlib'i yeniden yaz"
|
ilk müşteri (ADR-016). Batteries = sınır/FFI problemi, "zlib'i yeniden yaz"
|
||||||
değil; kripto asla elle yazılmaz (ADR-017).
|
değil; kripto asla elle yazılmaz (ADR-017).
|
||||||
|
|
@ -60,8 +88,11 @@ git'te izlenir.
|
||||||
## Belge haritası
|
## Belge haritası
|
||||||
- `readme.md` — toolbox çerçevesi, built-vs-planned, dil kimliği, çalıştırma modeli.
|
- `readme.md` — toolbox çerçevesi, built-vs-planned, dil kimliği, çalıştırma modeli.
|
||||||
- `docs/fikirler.md` — ADR-001…005 (backend stratejisi, parser, header-only, token, IR).
|
- `docs/fikirler.md` — ADR-001…005 (backend stratejisi, parser, header-only, token, IR).
|
||||||
- `docs/adr-frontend-analiz.md` — ADR-006…019 (frontend, analiz/optimizasyon,
|
- `docs/adr-frontend-analiz.md` — ADR-006…024 (frontend, analiz/optimizasyon,
|
||||||
çalıştırma modeli, FFI, interface, bellek).
|
çalıştırma modeli, FFI, interface, bellek, **değer/referans semantiği, null
|
||||||
|
güvenliği, mark-sweep GC, eşitlik, string**).
|
||||||
|
- `docs/sonnet-handoff.md` — **Sonnet için uygulama promptu** (ADR-020…024'ü koda
|
||||||
|
döken sıralı görev planı; ilk görev: GC-hazır nesne modeli + array runtime).
|
||||||
- `docs/roadmap-frontend.md` — faz-faz uygulama planı (Faz 0–4 → fibonacci).
|
- `docs/roadmap-frontend.md` — faz-faz uygulama planı (Faz 0–4 → fibonacci).
|
||||||
- `docs/transkript-frontend-tasarim.md` — tasarım oturumu transkripti.
|
- `docs/transkript-frontend-tasarim.md` — tasarım oturumu transkripti.
|
||||||
- `examples/fibonacci.sqt` — geçerli referans program.
|
- `examples/fibonacci.sqt` — geçerli referans program.
|
||||||
|
|
|
||||||
23
TODO.md
23
TODO.md
|
|
@ -5,6 +5,29 @@ tasarım noktalarını tutar. Her giriş hangi issue'ya bağlı olduğunu belirt
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## ✅ TAMAMLANDI — GC-hazır nesne modeli + array runtime (2026-06-20)
|
||||||
|
|
||||||
|
ADR-020…024 (değer/referans semantiği, null güvenliği, mark-sweep GC, eşitlik)
|
||||||
|
doğrultusunda uçtan uca array çalışıyor:
|
||||||
|
- `src/vm/object.hpp` — Object, ArrayObject, Heap (v1: toplama yok, GC-hazır header)
|
||||||
|
- `src/vm/value.hpp` — ValueKind::Ref + Nil eklendi
|
||||||
|
- `src/ir/instruction.hpp` — ARRAY_NEW/GET/SET/LEN eklendi
|
||||||
|
- Array literal parser (`[1,2,3]`), `int[]` tip sözdizimi (Java/C# stili)
|
||||||
|
- Referans semantiği, kimlik `==` (ADR-023), sınır kontrolü
|
||||||
|
- Golden test: `tests/golden/array/ref_semantics.sqt` ✓
|
||||||
|
|
||||||
|
## 🚀 SIRADAKİ İŞ (docs/sonnet-handoff.md Bölüm 2)
|
||||||
|
|
||||||
|
1. **Struct runtime** — StructObject : Object, alan erişimi, E010 revizyonu
|
||||||
|
2. **String cilası (ADR-024)** — içerik `==`, UTF-8 concat+print
|
||||||
|
3. **Null akış-analizi (ADR-021)** — `Type?`, flow-narrowing, `a!`
|
||||||
|
4. **float/double (#44)** — Value::Float + FADD/FSUB/… opcodes
|
||||||
|
5. **mark-sweep GC v2 (#56)** — header+kök kancası üstünde aç
|
||||||
|
|
||||||
|
Açık mimari borç: **#56** (döngüsel referans sızıntısı → mark-sweep GC v2).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## #modül-scope — IRFunction.moduleId: Modül-düzeyi değişken izolasyonu
|
## #modül-scope — IRFunction.moduleId: Modül-düzeyi değişken izolasyonu
|
||||||
|
|
||||||
**Etkilenen dosyalar:**
|
**Etkilenen dosyalar:**
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,154 @@
|
||||||
|
# Sonnet Uygulama Promptu — Bileşik Tipler Runtime'ı (ADR-020…024)
|
||||||
|
|
||||||
|
> Bu dosya bir **devir teslim promptu**. Opus ile yapılan mimari oturumda 5 karar
|
||||||
|
> alındı (ADR-020…024). Bu plan o kararları **koda döker.** Kararları yeniden
|
||||||
|
> sorma — uygula. İletişim Türkçe; commit sonu `Co-Authored-By: Claude Opus 4.8
|
||||||
|
> <noreply@anthropic.com>`; dal `0.1.0`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 0. Nerede kaldık (bağlam)
|
||||||
|
|
||||||
|
Bu oturumda kilitlenen mimari kararlar (tümü `docs/adr-frontend-analiz.md`):
|
||||||
|
|
||||||
|
| ADR | Karar (özet) |
|
||||||
|
|---|---|
|
||||||
|
| **020** | Primitive (`int`/`float`/`bool`) = **değer**; bileşik (`struct`/`array`/`string`) = **referans** (JS/Java/C# modeli). "Pointer yok" = kullanıcıya `&`/`*` *sözdizimi* yok; runtime referansı kullanır. |
|
||||||
|
| **021** | Null güvenliği: varsayılan **non-null**; nullable açıkça `Type?`. Akış-duyarlı null analizi (compile-time, runtime sıfır). `a!` = runtime-kontrollü iddia. |
|
||||||
|
| **022** | GC: basit **taşımasız, stop-the-world, deterministik mark-sweep**. Nesne modeli **baştan GC-hazır** (header + kök sayımı + çocuk sayımı). v1: toplama YOK. **`shared_ptr`'ı kalıcı sahiplik modeli YAPMA.** |
|
||||||
|
| **023** | `==`: primitive değer; referans (struct/array) **kimlik** (aynı nesne); string **içerik**. Derin eşitlik asla `==`'e bağlanmaz → ayrı `deepEquals()`. |
|
||||||
|
| **024** | String = **immutable değer-tipi, iç temsil UTF-8**. `s+"x"` yeni string üretir; `==` içerik. |
|
||||||
|
|
||||||
|
Açık mimari borç: **#56** (döngüsel referans → mark-sweep GC v2). Şimdilik toplama yok, bilinçli.
|
||||||
|
|
||||||
|
**Mevcut kod durumu:**
|
||||||
|
- int-only pipeline uçtan uca çalışıyor (`examples/fibonacci.sqt`).
|
||||||
|
- `src/vm/value.hpp` → `Value` = `{ ValueKind kind; int intValue; std::string stringValue; }`. String **inline** (immutable olduğu için yeterli — taşıma zorunlu değil).
|
||||||
|
- `src/ir/` → 3-adresli IR, slot tabanlı. `instruction.hpp`, `ir_generator.cpp`, `ir_program.hpp`.
|
||||||
|
- `src/vm/interpreter.cpp` → bytecode yorumlayıcı döngü. `call_frame.hpp` çağrı çerçevesi.
|
||||||
|
- struct/array: parser + semantik **var**, IR/VM codegen **YOK**.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. İlk görev — GC-hazır nesne modeli + ARRAY runtime (dikey dilim)
|
||||||
|
|
||||||
|
**Neden array, string değil:** string zaten immutable-değer olarak inline çalışıyor;
|
||||||
|
asıl kararları (referans semantiği + GC-hazır nesne modeli + kimlik eşitliği)
|
||||||
|
**doğrulayan** ilk gerçek referans-tipi array'dir. Bu görev, struct'ın da oturacağı
|
||||||
|
temeli atar.
|
||||||
|
|
||||||
|
### KAPSAM DIŞI (bu görevde YAPMA — sonraki görevler)
|
||||||
|
- ❌ Gerçek çöp toplama (mark-sweep). ADR-022 v1 = toplama yok. Sadece header +
|
||||||
|
all-objects listesi + kök-sayımı *kancasını* kur.
|
||||||
|
- ❌ Tam null akış-analizi (ADR-021). `int[]?` parse edilebilir ama flow-narrowing
|
||||||
|
bu görevde değil.
|
||||||
|
- ❌ struct codegen, string UTF-8 cilası, builder. Ayrı görevler (Bölüm 2).
|
||||||
|
- ❌ `shared_ptr` ile nesne sahipliği. Düz `Object*` + intrusive liste.
|
||||||
|
|
||||||
|
### Adım 1.1 — Nesne modeli temeli (`src/vm/object.hpp` veya benzeri)
|
||||||
|
```cpp
|
||||||
|
enum class ObjectType { Array /*, Struct, String (ileride) */ };
|
||||||
|
|
||||||
|
struct Object {
|
||||||
|
ObjectType type;
|
||||||
|
bool marked = false; // mark-sweep için (v2); şimdilik kullanılmaz
|
||||||
|
Object* next = nullptr; // intrusive "tüm nesneler" listesi (mark-sweep tarayışı için)
|
||||||
|
};
|
||||||
|
```
|
||||||
|
- Bir **Heap/allocator**: `Object* allocate(...)` her yeni nesneyi `next` zincirine
|
||||||
|
ekler. **Free yok** (v1). Yıkıcıda/process exit'te toptan bırak.
|
||||||
|
- **Kök sayımı kancası:** VM'in operand stack + frame local'leri + global slot'lar
|
||||||
|
üstünden referansları gezebileceği bir yol bırak (şimdilik imza/TODO yeterli; içini
|
||||||
|
doldurmak v2). `// TODO(#56): mark-sweep kök taraması buradan` ile işaretle.
|
||||||
|
|
||||||
|
### Adım 1.2 — `Value`'yu referans taşıyacak şekilde genişlet (`src/vm/value.hpp`)
|
||||||
|
- `ValueKind`'a `Ref` ekle (array/struct nesnelerine `Object*`).
|
||||||
|
- `Value`'ya `Object* ref = nullptr;` alanı + `static Value fromRef(Object*)`.
|
||||||
|
- `Nil` kind ekle (ADR-021 null: `Type?` null değeri ve referans varsayılanı).
|
||||||
|
- `isTruthy`/`toString`/`typeName`'i yeni kind'lar için genişlet.
|
||||||
|
- String inline kalsın (`ValueKind::String`), dokunma.
|
||||||
|
|
||||||
|
### Adım 1.3 — ArrayObject
|
||||||
|
```cpp
|
||||||
|
struct ArrayObject : Object { // type = ObjectType::Array
|
||||||
|
std::vector<Value> elements; // homojen (int[] → hepsi Int)
|
||||||
|
};
|
||||||
|
```
|
||||||
|
- Array literali (`[1,2,3]` — **parser'ın mevcut sözdizimini doğrula**, Bölüm 4'e bak)
|
||||||
|
→ ArrayObject tahsis, `elements` doldur, `Value::fromRef` ile slot'a koy.
|
||||||
|
|
||||||
|
### Adım 1.4 — IR opcode'ları + IR üreteci
|
||||||
|
- `ARRAY_NEW` (literal/boyuttan array oluştur), `ARRAY_GET dest, arr, idx`,
|
||||||
|
`ARRAY_SET arr, idx, val`, `ARRAY_LEN dest, arr`. (`instruction.hpp` + üreteç.)
|
||||||
|
- **Referans semantiği:** array bir fonksiyona geçince/atanınca `Value` (içindeki
|
||||||
|
`Object*`) **kopyalanır ama nesne paylaşılır** → `func(arr)` içindeki `ARRAY_SET`
|
||||||
|
çağıranı etkiler. (Bu, kararın doğrulandığı kritik testtir.)
|
||||||
|
- **`==` (ADR-023):** array'de **kimlik** → iki `Value`'nun `ref`'i aynı `Object*` mı?
|
||||||
|
(İçerik DEĞİL.) Mevcut EQ opcode'unu tip-bazlı dallandır veya `REF_EQ` ekle.
|
||||||
|
|
||||||
|
### Adım 1.5 — Sınır kontrolü (kafes felsefesi)
|
||||||
|
- `ARRAY_GET`/`ARRAY_SET` index sınır dışıysa → **temiz runtime hatası** (sessiz UB
|
||||||
|
değil). saQut "cage" — koruma şart.
|
||||||
|
|
||||||
|
### Doğrulama (golden test — `tests/` veya `examples/`)
|
||||||
|
```c
|
||||||
|
func degistir(int[] a) {
|
||||||
|
a[0] = 99;
|
||||||
|
}
|
||||||
|
func main() {
|
||||||
|
int[] x = [1, 2, 3];
|
||||||
|
degistir(x);
|
||||||
|
print(x[0]); // 99 ← referans semantiği (ADR-020)
|
||||||
|
int[] y = x;
|
||||||
|
print(y == x); // true ← kimlik (aynı nesne, ADR-023)
|
||||||
|
int[] z = [1, 2, 3];
|
||||||
|
print(z == x); // false ← içerikçe aynı ama farklı nesne
|
||||||
|
print(x[1]); // 2
|
||||||
|
// x[5] → runtime sınır hatası
|
||||||
|
}
|
||||||
|
```
|
||||||
|
`saqut run` ile beklenen çıktı doğrulanmalı. `saqut ir` çıktısı yeni opcode'ları göstermeli.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Sonraki görevler (sıralı — ilk görev bitince)
|
||||||
|
|
||||||
|
1. **Struct runtime.** Alanlar (isimli), `StructObject : Object { std::vector<Value> fields; }`.
|
||||||
|
`struct Node { Node next; }` artık **meşru** (referans → sonlu boyut). **E010
|
||||||
|
revizyonu:** referansla tutulan struct alanı için döngü artık hata DEĞİL (ADR-020).
|
||||||
|
Bu görev #56'yı *canlı* hale getirir (döngü kurulabilir, henüz toplanmaz).
|
||||||
|
2. **String cilası (ADR-024).** İçerik `==`'i doğrula; UTF-8 çok-baytlı pass-through
|
||||||
|
(`"şğü"` concat+print bozulmasın); bayt-uzunluğu vs karakter ayrımını açık API
|
||||||
|
olarak işaretle. (İstege bağlı: string'i de Object modeline taşıyıp intern et —
|
||||||
|
zorunlu değil.)
|
||||||
|
3. **Null akış-analizi (ADR-021).** Ayrı **frontend** görevi: `Type?` tip sistemi +
|
||||||
|
akış-duyarlı narrowing (`if (a != null)`, guard, `&&`), `T?` üstünde doğrudan
|
||||||
|
erişim → derleme hatası, `a!` runtime-kontrollü iddia. CFG/yapısal akış üstünde;
|
||||||
|
SSA gerekmez. #20 (akıllı diagnostic) buradan beslenir.
|
||||||
|
4. **float/double (#44).** Bağımsız; `Value::Float` + FADD… opcode + tip denetleyici.
|
||||||
|
5. **mark-sweep GC v2 (#56).** Adım 1.1'de bırakılan header+kök kancası üstünde aç.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Uyman gereken kararlar (sorma, uygula)
|
||||||
|
- Referans semantiği: bileşik = paylaşılan nesne; primitive = kopya (ADR-020).
|
||||||
|
- Array/struct `==` = **kimlik**; string `==` = **içerik** (ADR-023).
|
||||||
|
- Nesne modeli GC-hazır ama v1 toplama YOK (ADR-022); `shared_ptr` sahiplik modeli kurma.
|
||||||
|
- Sınır kontrolü + temiz runtime hataları (kafes felsefesi).
|
||||||
|
- Erken soyutlama yok: önce çalışan dikey dilim, framework sonra (CLAUDE.md ilkesi).
|
||||||
|
|
||||||
|
## 4. Bloklamayan açık noktalar (ilk göreve engel değil — gerekirse Opus'a sor)
|
||||||
|
- **Array literal sözdizimi:** parser'da `[1,2,3]` mi `{0,1,2}` mi kabul ediliyor?
|
||||||
|
Mevcut parser'ı **doğrula**; tutarsızsa `[...]` tercih (tip `int[]`). Karar gerekirse işaretle.
|
||||||
|
- Array sabit-boyut mu büyüyebilir mi: readme "dinamik" der; ilk görevde literal+indeks
|
||||||
|
yeter, büyütme API'si (`push`/`len`) sonra.
|
||||||
|
- `int[]` non-null varsayılan (ADR-021) → `int[] a;` init zorunlu; nullable `int[]?`.
|
||||||
|
- #42 (cast modeli) ve #41 (stdlib politikası) henüz karara bağlanmadı — ilk görevi
|
||||||
|
bloklamaz; sıraları gelince Opus ile.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Özet — tek cümle
|
||||||
|
GC-hazır basit nesne modelini kur (header + all-objects listesi, toplama yok), `Value`'ya
|
||||||
|
referans (`Ref`) + `Nil` ekle, **array**'i referans semantiği + kimlik `==` + sınır
|
||||||
|
kontrolüyle uçtan uca çalıştır; struct ve null-analizi sonraki görevler.
|
||||||
|
|
@ -188,6 +188,11 @@ struct Type {
|
||||||
if (n == "string") return String();
|
if (n == "string") return String();
|
||||||
if (n == "bool") return Bool();
|
if (n == "bool") return Bool();
|
||||||
if (n == "void") return Void();
|
if (n == "void") return Void();
|
||||||
|
// "int[]", "float[]" vb. — suffix [] ile dizi tipi
|
||||||
|
if (n.size() > 2 && n.substr(n.size() - 2) == "[]") {
|
||||||
|
Type elem = fromName(n.substr(0, n.size() - 2));
|
||||||
|
if (!elem.isError()) return array(elem);
|
||||||
|
}
|
||||||
return error();
|
return error();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -82,6 +82,12 @@ enum class Opcode {
|
||||||
|
|
||||||
RETURN, // Bu frame'i kapat, slots[src]'yi caller'a ilet.
|
RETURN, // Bu frame'i kapat, slots[src]'yi caller'a ilet.
|
||||||
|
|
||||||
|
// --- Array (ADR-020: referans semantiği) ---
|
||||||
|
ARRAY_NEW, // slots[dest] = yeni ArrayObject(intValue eleman kapasitesi)
|
||||||
|
ARRAY_GET, // slots[dest] = slots[left][slots[right]] — sınır kontrolü
|
||||||
|
ARRAY_SET, // slots[dest][slots[left]] = slots[right] — sınır kontrolü (dest=dizi, left=idx, right=değer)
|
||||||
|
ARRAY_LEN, // slots[dest] = slots[src].uzunluk()
|
||||||
|
|
||||||
// --- Modül-düzeyi değişken erişimi ---
|
// --- Modül-düzeyi değişken erişimi ---
|
||||||
// "Global" değil: her değişken kendi dosyasına (modülüne) aittir.
|
// "Global" değil: her değişken kendi dosyasına (modülüne) aittir.
|
||||||
// Başka modüller bu alana doğrudan erişemez; yalnızca export/import ile ulaşabilir.
|
// Başka modüller bu alana doğrudan erişemez; yalnızca export/import ile ulaşabilir.
|
||||||
|
|
@ -111,6 +117,10 @@ inline const char* opcodeName(Opcode op) {
|
||||||
case Opcode::SHL: return "SHL";
|
case Opcode::SHL: return "SHL";
|
||||||
case Opcode::SHR: return "SHR";
|
case Opcode::SHR: return "SHR";
|
||||||
case Opcode::BNOT: return "BNOT";
|
case Opcode::BNOT: return "BNOT";
|
||||||
|
case Opcode::ARRAY_NEW: return "ARRAY_NEW";
|
||||||
|
case Opcode::ARRAY_GET: return "ARRAY_GET";
|
||||||
|
case Opcode::ARRAY_SET: return "ARRAY_SET";
|
||||||
|
case Opcode::ARRAY_LEN: return "ARRAY_LEN";
|
||||||
case Opcode::LOAD_GLOBAL: return "LOAD_GLOBAL";
|
case Opcode::LOAD_GLOBAL: return "LOAD_GLOBAL";
|
||||||
case Opcode::STORE_GLOBAL: return "STORE_GLOBAL";
|
case Opcode::STORE_GLOBAL: return "STORE_GLOBAL";
|
||||||
case Opcode::LESS: return "LESS";
|
case Opcode::LESS: return "LESS";
|
||||||
|
|
|
||||||
|
|
@ -121,6 +121,18 @@ void IRFunction::dump() const {
|
||||||
} else if (ins.opcode == Opcode::BNOT) {
|
} else if (ins.opcode == Opcode::BNOT) {
|
||||||
std::cout << slot(ins.dest) << " = ~" << slot(ins.src);
|
std::cout << slot(ins.dest) << " = ~" << slot(ins.src);
|
||||||
|
|
||||||
|
} else if (ins.opcode == Opcode::ARRAY_NEW) {
|
||||||
|
std::cout << slot(ins.dest) << " = array[" << ins.intValue << "]";
|
||||||
|
|
||||||
|
} else if (ins.opcode == Opcode::ARRAY_GET) {
|
||||||
|
std::cout << slot(ins.dest) << " = " << slot(ins.left) << "[" << slot(ins.right) << "]";
|
||||||
|
|
||||||
|
} else if (ins.opcode == Opcode::ARRAY_SET) {
|
||||||
|
std::cout << slot(ins.dest) << "[" << slot(ins.left) << "] = " << slot(ins.right);
|
||||||
|
|
||||||
|
} else if (ins.opcode == Opcode::ARRAY_LEN) {
|
||||||
|
std::cout << slot(ins.dest) << " = len(" << slot(ins.src) << ")";
|
||||||
|
|
||||||
} else if (ins.opcode == Opcode::LOAD_GLOBAL) {
|
} else if (ins.opcode == Opcode::LOAD_GLOBAL) {
|
||||||
std::cout << slot(ins.dest) << " = global[" << ins.intValue << "]";
|
std::cout << slot(ins.dest) << " = global[" << ins.intValue << "]";
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -376,9 +376,19 @@ int IRGenerator::generateExpression(ASTNode* node) {
|
||||||
case ASTKind::BinaryExpression: {
|
case ASTKind::BinaryExpression: {
|
||||||
auto* bin = (BinaryExpressionNode*)node;
|
auto* bin = (BinaryExpressionNode*)node;
|
||||||
|
|
||||||
// Atama operatörleri: x = expr
|
// Atama operatörleri: x = expr ve a[i] = expr
|
||||||
if (bin->Operator == TokenType::EQUAL) {
|
if (bin->Operator == TokenType::EQUAL) {
|
||||||
int rhsSlot = generateExpression(bin->Right);
|
int rhsSlot = generateExpression(bin->Right);
|
||||||
|
|
||||||
|
// a[i] = val → ARRAY_SET
|
||||||
|
if (bin->Left && bin->Left->kind == ASTKind::IndexExpression) {
|
||||||
|
auto* idx = (IndexExpressionNode*)bin->Left;
|
||||||
|
int arrSlot = generateExpression(idx->object);
|
||||||
|
int idxSlot = generateExpression(idx->index);
|
||||||
|
emitArraySet(arrSlot, idxSlot, rhsSlot);
|
||||||
|
return rhsSlot;
|
||||||
|
}
|
||||||
|
|
||||||
auto* lhsId = (IdentifierNode*)bin->Left;
|
auto* lhsId = (IdentifierNode*)bin->Left;
|
||||||
std::string varName = lhsId->parserToken.token->token;
|
std::string varName = lhsId->parserToken.token->token;
|
||||||
|
|
||||||
|
|
@ -579,6 +589,30 @@ int IRGenerator::generateExpression(ASTNode* node) {
|
||||||
return resultSlot; // artırmadan önceki değer
|
return resultSlot; // artırmadan önceki değer
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ── Array literali: [1, 2, 3] ─────────────────────────────────────────
|
||||||
|
case ASTKind::ArrayLiteral: {
|
||||||
|
auto* al = (ArrayLiteralNode*)node;
|
||||||
|
int arrSlot = freshSlot();
|
||||||
|
emitArrayNew(arrSlot, (int)al->elements.size());
|
||||||
|
for (int i = 0; i < (int)al->elements.size(); i++) {
|
||||||
|
int idxSlot = freshSlot();
|
||||||
|
emitLoadConst(idxSlot, i);
|
||||||
|
int valSlot = generateExpression(al->elements[i]);
|
||||||
|
emitArraySet(arrSlot, idxSlot, valSlot);
|
||||||
|
}
|
||||||
|
return arrSlot;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Index erişimi okuma: a[i] ─────────────────────────────────────────
|
||||||
|
case ASTKind::IndexExpression: {
|
||||||
|
auto* idx = (IndexExpressionNode*)node;
|
||||||
|
int arrSlot = generateExpression(idx->object);
|
||||||
|
int idxSlot = generateExpression(idx->index);
|
||||||
|
int destSlot = freshSlot();
|
||||||
|
emitArrayGet(destSlot, arrSlot, idxSlot);
|
||||||
|
return destSlot;
|
||||||
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
// Bilinmeyen ifade türü
|
// Bilinmeyen ifade türü
|
||||||
return freshSlot(); // boş slot (0 değeriyle)
|
return freshSlot(); // boş slot (0 değeriyle)
|
||||||
|
|
@ -651,6 +685,36 @@ void IRGenerator::emitStoreGlobal(int srcSlot, int globalIndex) {
|
||||||
currentFunction_->instructions.push_back(std::move(ins));
|
currentFunction_->instructions.push_back(std::move(ins));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void IRGenerator::emitArrayNew(int destSlot, int capacity) {
|
||||||
|
Instruction ins(Opcode::ARRAY_NEW);
|
||||||
|
ins.dest = destSlot;
|
||||||
|
ins.intValue = capacity;
|
||||||
|
currentFunction_->instructions.push_back(std::move(ins));
|
||||||
|
}
|
||||||
|
|
||||||
|
void IRGenerator::emitArrayGet(int destSlot, int arrSlot, int idxSlot) {
|
||||||
|
Instruction ins(Opcode::ARRAY_GET);
|
||||||
|
ins.dest = destSlot;
|
||||||
|
ins.left = arrSlot;
|
||||||
|
ins.right = idxSlot;
|
||||||
|
currentFunction_->instructions.push_back(std::move(ins));
|
||||||
|
}
|
||||||
|
|
||||||
|
void IRGenerator::emitArraySet(int arrSlot, int idxSlot, int valSlot) {
|
||||||
|
Instruction ins(Opcode::ARRAY_SET);
|
||||||
|
ins.dest = arrSlot;
|
||||||
|
ins.left = idxSlot;
|
||||||
|
ins.right = valSlot;
|
||||||
|
currentFunction_->instructions.push_back(std::move(ins));
|
||||||
|
}
|
||||||
|
|
||||||
|
void IRGenerator::emitArrayLen(int destSlot, int arrSlot) {
|
||||||
|
Instruction ins(Opcode::ARRAY_LEN);
|
||||||
|
ins.dest = destSlot;
|
||||||
|
ins.src = arrSlot;
|
||||||
|
currentFunction_->instructions.push_back(std::move(ins));
|
||||||
|
}
|
||||||
|
|
||||||
bool IRGenerator::isGlobal(const std::string& name) const {
|
bool IRGenerator::isGlobal(const std::string& name) const {
|
||||||
return nameToGlobal_.count(name) > 0;
|
return nameToGlobal_.count(name) > 0;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -58,6 +58,10 @@ private:
|
||||||
void emitLoadSlot(int destSlot, int srcSlot);
|
void emitLoadSlot(int destSlot, int srcSlot);
|
||||||
void emitLoadGlobal(int destSlot, int globalIndex);
|
void emitLoadGlobal(int destSlot, int globalIndex);
|
||||||
void emitStoreGlobal(int srcSlot, int globalIndex);
|
void emitStoreGlobal(int srcSlot, int globalIndex);
|
||||||
|
void emitArrayNew(int destSlot, int capacity);
|
||||||
|
void emitArrayGet(int destSlot, int arrSlot, int idxSlot);
|
||||||
|
void emitArraySet(int arrSlot, int idxSlot, int valSlot);
|
||||||
|
void emitArrayLen(int destSlot, int arrSlot);
|
||||||
void emitBinaryOp(Opcode op, int destSlot, int leftSlot, int rightSlot);
|
void emitBinaryOp(Opcode op, int destSlot, int leftSlot, int rightSlot);
|
||||||
void emitReturn(int srcSlot);
|
void emitReturn(int srcSlot);
|
||||||
// Koşulsuz atlama yazar; instruction indeksini döndürür (backpatch için).
|
// Koşulsuz atlama yazar; instruction indeksini döndürür (backpatch için).
|
||||||
|
|
|
||||||
|
|
@ -107,6 +107,8 @@ enum class ASTKind {
|
||||||
// children: [object], [member]
|
// children: [object], [member]
|
||||||
IndexExpression, // Dizi/indeks erişimi: a[i].
|
IndexExpression, // Dizi/indeks erişimi: a[i].
|
||||||
// children: [object], [index]
|
// children: [object], [index]
|
||||||
|
ArrayLiteral, // Dizi literali: [1, 2, 3].
|
||||||
|
// children: [element0, element1, ...]
|
||||||
};
|
};
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|
|
||||||
|
|
@ -53,6 +53,23 @@ std::string MemberAccessNode::toJson(int depth) {
|
||||||
return obj.str();
|
return obj.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ArrayLiteralNode
|
||||||
|
ArrayLiteralNode::ArrayLiteralNode() { kind = ASTKind::ArrayLiteral; }
|
||||||
|
void ArrayLiteralNode::log(int indent) {
|
||||||
|
std::cout << jsonIndent(indent) << "ArrayLiteral [" << elements.size() << " eleman]\n";
|
||||||
|
for (auto* e : elements) e->log(indent + 1);
|
||||||
|
}
|
||||||
|
std::string ArrayLiteralNode::toJson(int depth) {
|
||||||
|
JsonObject obj(depth);
|
||||||
|
obj.add("kind", "ArrayLiteral");
|
||||||
|
obj.addArray("elements", [&]() {
|
||||||
|
for (auto* e : elements) obj.addItem(e->toJson(depth + 2));
|
||||||
|
});
|
||||||
|
obj.addRaw("resolvedType", resolvedTypeJson());
|
||||||
|
obj.addRaw("location", loc.toJson());
|
||||||
|
return obj.str();
|
||||||
|
}
|
||||||
|
|
||||||
// IndexExpressionNode
|
// IndexExpressionNode
|
||||||
IndexExpressionNode::IndexExpressionNode() { kind = ASTKind::IndexExpression; }
|
IndexExpressionNode::IndexExpressionNode() { kind = ASTKind::IndexExpression; }
|
||||||
void IndexExpressionNode::log(int indent) {
|
void IndexExpressionNode::log(int indent) {
|
||||||
|
|
|
||||||
|
|
@ -44,4 +44,13 @@ public:
|
||||||
std::string toJson(int depth = 0) override;
|
std::string toJson(int depth = 0) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class ArrayLiteralNode : public ExpressionNode {
|
||||||
|
public:
|
||||||
|
std::vector<ASTNode*> elements;
|
||||||
|
ArrayLiteralNode();
|
||||||
|
~ArrayLiteralNode() override { for (auto* e : elements) delete e; }
|
||||||
|
void log(int indent = 0) override;
|
||||||
|
std::string toJson(int depth = 0) override;
|
||||||
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -150,6 +150,23 @@ ASTNode* Parser::parseNullDenotation() {
|
||||||
return expr;
|
return expr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Array literal: [expr, expr, ...]
|
||||||
|
if (ct.type == TokenType::LBRACKET) {
|
||||||
|
nextToken();
|
||||||
|
ArrayLiteralNode* arr = new ArrayLiteralNode();
|
||||||
|
arr->loc = ct.token ? ct.token->loc : SourceLocation{};
|
||||||
|
if (currentToken().type != TokenType::RBRACKET) {
|
||||||
|
arr->elements.push_back(parseExpression(0));
|
||||||
|
while (currentToken().type == TokenType::COMMA) {
|
||||||
|
nextToken();
|
||||||
|
arr->elements.push_back(parseExpression(0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (currentToken().type == TokenType::RBRACKET)
|
||||||
|
nextToken();
|
||||||
|
return arr;
|
||||||
|
}
|
||||||
|
|
||||||
if (ct.is({
|
if (ct.is({
|
||||||
TokenType::PLUS_PLUS, TokenType::MINUS_MINUS,
|
TokenType::PLUS_PLUS, TokenType::MINUS_MINUS,
|
||||||
TokenType::PLUS, TokenType::MINUS,
|
TokenType::PLUS, TokenType::MINUS,
|
||||||
|
|
@ -315,6 +332,13 @@ ASTNode* Parser::parseFunctionDecl() {
|
||||||
if (!isTypeKw || !typeTok.token) break;
|
if (!isTypeKw || !typeTok.token) break;
|
||||||
std::string paramType = typeTok.token->token;
|
std::string paramType = typeTok.token->token;
|
||||||
nextToken();
|
nextToken();
|
||||||
|
// int[] a — tip sonrasında [] varsa array tipi
|
||||||
|
if (currentToken().type == TokenType::LBRACKET) {
|
||||||
|
nextToken();
|
||||||
|
if (currentToken().type == TokenType::RBRACKET)
|
||||||
|
nextToken();
|
||||||
|
paramType += "[]";
|
||||||
|
}
|
||||||
if (currentToken().type != TokenType::IDENTIFIER || !currentToken().token) break;
|
if (currentToken().type != TokenType::IDENTIFIER || !currentToken().token) break;
|
||||||
VariableDeclNode* param = new VariableDeclNode();
|
VariableDeclNode* param = new VariableDeclNode();
|
||||||
param->loc = currentToken().token->loc;
|
param->loc = currentToken().token->loc;
|
||||||
|
|
@ -364,6 +388,14 @@ ASTNode* Parser::parseVariableDecl() {
|
||||||
vd->varType = currentToken().token->token;
|
vd->varType = currentToken().token->token;
|
||||||
nextToken();
|
nextToken();
|
||||||
|
|
||||||
|
// Java/C# stili: int[] x — tip adından hemen sonra [] gelir
|
||||||
|
if (currentToken().type == TokenType::LBRACKET) {
|
||||||
|
nextToken();
|
||||||
|
if (currentToken().type == TokenType::RBRACKET)
|
||||||
|
nextToken();
|
||||||
|
vd->varType += "[]";
|
||||||
|
}
|
||||||
|
|
||||||
if (currentToken().type != TokenType::IDENTIFIER) {
|
if (currentToken().type != TokenType::IDENTIFIER) {
|
||||||
std::cerr << "Parser hatası: değişken ismi bekleniyor\n";
|
std::cerr << "Parser hatası: değişken ismi bekleniyor\n";
|
||||||
return vd;
|
return vd;
|
||||||
|
|
@ -372,6 +404,7 @@ ASTNode* Parser::parseVariableDecl() {
|
||||||
vd->name = currentToken().token->token;
|
vd->name = currentToken().token->token;
|
||||||
nextToken();
|
nextToken();
|
||||||
|
|
||||||
|
// C stili: int x[] — geriye dönük uyumluluk (postfix [])
|
||||||
if (currentToken().type == TokenType::LBRACKET) {
|
if (currentToken().type == TokenType::LBRACKET) {
|
||||||
nextToken();
|
nextToken();
|
||||||
while (currentToken().type != TokenType::RBRACKET &&
|
while (currentToken().type != TokenType::RBRACKET &&
|
||||||
|
|
@ -380,6 +413,7 @@ ASTNode* Parser::parseVariableDecl() {
|
||||||
nextToken();
|
nextToken();
|
||||||
if (currentToken().type == TokenType::RBRACKET)
|
if (currentToken().type == TokenType::RBRACKET)
|
||||||
nextToken();
|
nextToken();
|
||||||
|
if (vd->varType.back() != ']') vd->varType += "[]";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (currentToken().type == TokenType::EQUAL) {
|
if (currentToken().type == TokenType::EQUAL) {
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
#include "vm/interpreter.hpp"
|
#include "vm/interpreter.hpp"
|
||||||
|
#include "vm/object.hpp"
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
|
||||||
|
|
@ -121,17 +122,25 @@ int Interpreter::run() {
|
||||||
break;
|
break;
|
||||||
case Opcode::EQUAL_EQUAL: {
|
case Opcode::EQUAL_EQUAL: {
|
||||||
auto& lv = frame.slots[instr.left]; auto& rv = frame.slots[instr.right];
|
auto& lv = frame.slots[instr.left]; auto& rv = frame.slots[instr.right];
|
||||||
int r = (lv.kind == ValueKind::String)
|
int r;
|
||||||
? (lv.stringValue == rv.stringValue ? 1 : 0)
|
if (lv.kind == ValueKind::Ref || rv.kind == ValueKind::Ref)
|
||||||
: (lv.intValue == rv.intValue ? 1 : 0);
|
r = (lv.ref == rv.ref ? 1 : 0); // ADR-023: array/struct kimlik karşılaştırması
|
||||||
|
else if (lv.kind == ValueKind::String)
|
||||||
|
r = (lv.stringValue == rv.stringValue ? 1 : 0);
|
||||||
|
else
|
||||||
|
r = (lv.intValue == rv.intValue ? 1 : 0);
|
||||||
frame.slots[instr.dest] = Value::fromInt(r);
|
frame.slots[instr.dest] = Value::fromInt(r);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Opcode::NOT_EQUAL: {
|
case Opcode::NOT_EQUAL: {
|
||||||
auto& lv = frame.slots[instr.left]; auto& rv = frame.slots[instr.right];
|
auto& lv = frame.slots[instr.left]; auto& rv = frame.slots[instr.right];
|
||||||
int r = (lv.kind == ValueKind::String)
|
int r;
|
||||||
? (lv.stringValue != rv.stringValue ? 1 : 0)
|
if (lv.kind == ValueKind::Ref || rv.kind == ValueKind::Ref)
|
||||||
: (lv.intValue != rv.intValue ? 1 : 0);
|
r = (lv.ref != rv.ref ? 1 : 0);
|
||||||
|
else if (lv.kind == ValueKind::String)
|
||||||
|
r = (lv.stringValue != rv.stringValue ? 1 : 0);
|
||||||
|
else
|
||||||
|
r = (lv.intValue != rv.intValue ? 1 : 0);
|
||||||
frame.slots[instr.dest] = Value::fromInt(r);
|
frame.slots[instr.dest] = Value::fromInt(r);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -184,6 +193,48 @@ int Interpreter::run() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ── Array (ADR-020: referans semantiği) ───────────────────────────
|
||||||
|
case Opcode::ARRAY_NEW: {
|
||||||
|
ArrayObject* arr = heap_.allocArray(instr.intValue);
|
||||||
|
arr->elements.resize(instr.intValue, Value::fromInt(0));
|
||||||
|
frame.slots[instr.dest] = Value::fromRef(arr);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Opcode::ARRAY_GET: {
|
||||||
|
Value& arrVal = frame.slots[instr.left];
|
||||||
|
if (arrVal.kind != ValueKind::Ref || !arrVal.ref)
|
||||||
|
throw std::runtime_error("Çalışma hatası: dizi değil");
|
||||||
|
auto* arr = (ArrayObject*)arrVal.ref;
|
||||||
|
int idx = frame.slots[instr.right].intValue;
|
||||||
|
if (idx < 0 || idx >= (int)arr->elements.size())
|
||||||
|
throw std::runtime_error(
|
||||||
|
"Çalışma hatası: dizi sınır dışı (indeks=" + std::to_string(idx) +
|
||||||
|
", uzunluk=" + std::to_string(arr->elements.size()) + ")");
|
||||||
|
frame.slots[instr.dest] = arr->elements[idx];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Opcode::ARRAY_SET: {
|
||||||
|
Value& arrVal = frame.slots[instr.dest];
|
||||||
|
if (arrVal.kind != ValueKind::Ref || !arrVal.ref)
|
||||||
|
throw std::runtime_error("Çalışma hatası: dizi değil");
|
||||||
|
auto* arr = (ArrayObject*)arrVal.ref;
|
||||||
|
int idx = frame.slots[instr.left].intValue;
|
||||||
|
if (idx < 0 || idx >= (int)arr->elements.size())
|
||||||
|
throw std::runtime_error(
|
||||||
|
"Çalışma hatası: dizi sınır dışı (indeks=" + std::to_string(idx) +
|
||||||
|
", uzunluk=" + std::to_string(arr->elements.size()) + ")");
|
||||||
|
arr->elements[idx] = frame.slots[instr.right];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Opcode::ARRAY_LEN: {
|
||||||
|
Value& arrVal = frame.slots[instr.src];
|
||||||
|
if (arrVal.kind != ValueKind::Ref || !arrVal.ref)
|
||||||
|
throw std::runtime_error("Çalışma hatası: dizi değil");
|
||||||
|
auto* arr = (ArrayObject*)arrVal.ref;
|
||||||
|
frame.slots[instr.dest] = Value::fromInt((int)arr->elements.size());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
// ── FFI ───────────────────────────────────────────────────────────
|
// ── FFI ───────────────────────────────────────────────────────────
|
||||||
case Opcode::CALLHOST:
|
case Opcode::CALLHOST:
|
||||||
executeHostFunction(instr.functionName, frame.slots, instr.argSlots);
|
executeHostFunction(instr.functionName, frame.slots, instr.argSlots);
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include "ir/ir_program.hpp"
|
#include "ir/ir_program.hpp"
|
||||||
#include "vm/call_frame.hpp"
|
#include "vm/call_frame.hpp"
|
||||||
|
#include "vm/object.hpp"
|
||||||
|
|
||||||
class Interpreter {
|
class Interpreter {
|
||||||
public:
|
public:
|
||||||
|
|
@ -29,6 +30,7 @@ private:
|
||||||
IRProgram& program_;
|
IRProgram& program_;
|
||||||
std::vector<CallFrame> callStack_;
|
std::vector<CallFrame> callStack_;
|
||||||
std::vector<Value> globalSlots_;
|
std::vector<Value> globalSlots_;
|
||||||
|
Heap heap_;
|
||||||
|
|
||||||
// Host (C++) fonksiyon çağrısı — şu an sadece "print" destekli
|
// Host (C++) fonksiyon çağrısı — şu an sadece "print" destekli
|
||||||
void executeHostFunction(const std::string& name,
|
void executeHostFunction(const std::string& name,
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,62 @@
|
||||||
|
#ifndef SAQUT_VM_OBJECT
|
||||||
|
#define SAQUT_VM_OBJECT
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
// ADR-022: GC-hazır nesne modeli (v1: toplama yok, sadece header + liste kancası).
|
||||||
|
// Her heap nesnesi bu yapıdan türer.
|
||||||
|
// marked + next: mark-sweep için hazır; v1'de kullanılmaz.
|
||||||
|
// TODO(#56): mark-sweep v2 — Heap::collect() bu header'ı kullanacak.
|
||||||
|
|
||||||
|
enum class ObjectType { Array /*, Struct, String (ileride) */ };
|
||||||
|
|
||||||
|
struct Object {
|
||||||
|
ObjectType type;
|
||||||
|
bool marked = false; // mark-sweep için (v2); şimdilik kullanılmaz
|
||||||
|
Object* next = nullptr; // "tüm nesneler" intrusive listesi
|
||||||
|
};
|
||||||
|
|
||||||
|
// Forward declare — Value, Object*'ı taşır; Object, Value içerir.
|
||||||
|
// Gerçek tanım value.hpp'den sonra gelir; burada sadece forward.
|
||||||
|
struct Value;
|
||||||
|
|
||||||
|
struct ArrayObject : Object {
|
||||||
|
std::vector<Value> elements;
|
||||||
|
explicit ArrayObject(int capacity = 0) {
|
||||||
|
type = ObjectType::Array;
|
||||||
|
if (capacity > 0) elements.reserve(capacity);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// ── Heap ─────────────────────────────────────────────────────────────────────
|
||||||
|
// Tüm nesneleri intrusive listede tutar. v1'de serbest bırakma yok.
|
||||||
|
// TODO(#56): mark-sweep v2 — collect() kök taraması yapacak, ölü nesneleri silecek.
|
||||||
|
struct Heap {
|
||||||
|
Object* head = nullptr;
|
||||||
|
int allocCount = 0;
|
||||||
|
|
||||||
|
ArrayObject* allocArray(int capacity = 0) {
|
||||||
|
auto* obj = new ArrayObject(capacity);
|
||||||
|
obj->next = head;
|
||||||
|
head = obj;
|
||||||
|
allocCount++;
|
||||||
|
return obj;
|
||||||
|
// TODO(#56): mark-sweep kök taraması buradan — GC eşiği aşılınca tetikle
|
||||||
|
}
|
||||||
|
|
||||||
|
// v1: process exit'te OS toplar; yıkıcı tüm nesneleri siler.
|
||||||
|
~Heap() {
|
||||||
|
Object* cur = head;
|
||||||
|
while (cur) {
|
||||||
|
Object* nxt = cur->next;
|
||||||
|
delete cur;
|
||||||
|
cur = nxt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Heap() = default;
|
||||||
|
Heap(const Heap&) = delete;
|
||||||
|
Heap& operator=(const Heap&) = delete;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // SAQUT_VM_OBJECT
|
||||||
|
|
@ -4,59 +4,70 @@
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
|
||||||
// Çalışma zamanı değer tipi.
|
// Forward — Object tam tanımı object.hpp'de; Value onu pointer olarak taşır.
|
||||||
//
|
struct Object;
|
||||||
// Bool ayrı bir kind değil — boolean sonuçlar int olarak saklanır
|
|
||||||
// (0 = yanlış, sıfır-dışı = doğru; C geleneği, JIF_FALSE buna dayanır).
|
// ADR-020: Primitive (int/bool) = değer; bileşik (array/struct/string) = referans.
|
||||||
|
// ADR-021: Nil = nullable referansların null değeri (Type? için).
|
||||||
|
// Bool ayrı kind değil — boolean sonuçlar int olarak saklanır (0=yanlış, sıfır-dışı=doğru).
|
||||||
// Float henüz implement edilmedi — IR'de float opcode yok.
|
// Float henüz implement edilmedi — IR'de float opcode yok.
|
||||||
enum class ValueKind {
|
enum class ValueKind {
|
||||||
Int,
|
Int,
|
||||||
String,
|
String,
|
||||||
// Float, // TODO: float literal + aritmetik eklenince
|
Ref, // ADR-020: array/struct nesnesine Object* referansı
|
||||||
|
Nil, // ADR-021: nullable referansın null değeri
|
||||||
|
// Float, // TODO(#44)
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Value {
|
struct Value {
|
||||||
ValueKind kind = ValueKind::Int;
|
ValueKind kind = ValueKind::Int;
|
||||||
int intValue = 0;
|
int intValue = 0;
|
||||||
std::string stringValue; // yalnızca kind == String için geçerli
|
std::string stringValue; // yalnızca kind == String
|
||||||
|
Object* ref = nullptr; // yalnızca kind == Ref
|
||||||
|
|
||||||
static Value fromInt(int n) {
|
static Value fromInt(int n) {
|
||||||
Value v;
|
Value v; v.kind = ValueKind::Int; v.intValue = n; return v;
|
||||||
v.kind = ValueKind::Int;
|
|
||||||
v.intValue = n;
|
|
||||||
return v;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static Value fromString(std::string s) {
|
static Value fromString(std::string s) {
|
||||||
Value v;
|
Value v; v.kind = ValueKind::String; v.stringValue = std::move(s); return v;
|
||||||
v.kind = ValueKind::String;
|
|
||||||
v.stringValue = std::move(s);
|
|
||||||
return v;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// JIF_FALSE için: int 0 = yanlış, boş string = yanlış, diğer = doğru
|
static Value fromRef(Object* obj) {
|
||||||
|
Value v; v.kind = ValueKind::Ref; v.ref = obj; return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Value nil() {
|
||||||
|
Value v; v.kind = ValueKind::Nil; return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
// JIF_FALSE: int 0 / boş string / nil = yanlış; Ref her zaman doğru
|
||||||
bool isTruthy() const {
|
bool isTruthy() const {
|
||||||
switch (kind) {
|
switch (kind) {
|
||||||
case ValueKind::Int: return intValue != 0;
|
case ValueKind::Int: return intValue != 0;
|
||||||
case ValueKind::String: return !stringValue.empty();
|
case ValueKind::String: return !stringValue.empty();
|
||||||
|
case ValueKind::Ref: return ref != nullptr;
|
||||||
|
case ValueKind::Nil: return false;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Yazdırma ve hata mesajları için okunabilir temsil
|
|
||||||
std::string toString() const {
|
std::string toString() const {
|
||||||
switch (kind) {
|
switch (kind) {
|
||||||
case ValueKind::Int: return std::to_string(intValue);
|
case ValueKind::Int: return std::to_string(intValue);
|
||||||
case ValueKind::String: return stringValue;
|
case ValueKind::String: return stringValue;
|
||||||
|
case ValueKind::Ref: return "<array>";
|
||||||
|
case ValueKind::Nil: return "nil";
|
||||||
}
|
}
|
||||||
return "?";
|
return "?";
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tip adı — hata mesajları için
|
|
||||||
std::string typeName() const {
|
std::string typeName() const {
|
||||||
switch (kind) {
|
switch (kind) {
|
||||||
case ValueKind::Int: return "int";
|
case ValueKind::Int: return "int";
|
||||||
case ValueKind::String: return "string";
|
case ValueKind::String: return "string";
|
||||||
|
case ValueKind::Ref: return "array";
|
||||||
|
case ValueKind::Nil: return "nil";
|
||||||
}
|
}
|
||||||
return "?";
|
return "?";
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
99
|
||||||
|
1
|
||||||
|
0
|
||||||
|
2
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
void degistir(int[] a) {
|
||||||
|
a[0] = 99;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
int[] x = [1, 2, 3];
|
||||||
|
degistir(x);
|
||||||
|
print(x[0]);
|
||||||
|
int[] y = x;
|
||||||
|
print(y == x);
|
||||||
|
int[] z = [1, 2, 3];
|
||||||
|
print(z == x);
|
||||||
|
print(x[1]);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue