feat(array): GC-hazır nesne modeli + array runtime (ADR-020/022/023)
- src/vm/object.hpp: Object, ArrayObject, Heap (v1: toplama yok, mark-sweep kancaları hazır) - src/vm/value.hpp: ValueKind::Ref + Nil eklendi (ADR-020/021) - src/ir/instruction.hpp: ARRAY_NEW/GET/SET/LEN opcodes - src/ir/ir_generator: array literal + a[i] okuma/yazma codegen - src/parser: [1,2,3] array literal, int[] tip sözdizimi (Java/C# stili) - src/vm/interpreter: ARRAY_NEW/GET/SET/LEN, sınır kontrolü, referans semantiği - EQUAL_EQUAL/NOT_EQUAL: Ref türü için kimlik karşılaştırması (ADR-023) - type.hpp: Type::fromName "int[]" vb. array tipleri destekliyor - golden test: tests/golden/array/ref_semantics.sqt (referans semantiği, kimlik ==) - 20 golden test geçiyor Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
c5f62a3a9d
commit
f1cb983b69
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
|
||||
gerekirse libgccjit/LLVM'e bağlanılır (çok uzak). Bellek = host C++ heap; özel
|
||||
allocator yok. (ADR-015)
|
||||
- **Dil kimliği:** prosedürel, C-ailesi sözdizimi, value semantics, zorunlu
|
||||
class/main boilerplate yok. **Yok:** class/OOP, closure, generic, kullanıcı
|
||||
pointer'ı (`*`/`&`), auto/tip çıkarımı, gizli int↔float (tek istisna sabit
|
||||
folding). **Var:** struct, tipli fonksiyonlar, array (`int[]`). `interface`
|
||||
**ertelendi** (reddedilmedi, ADR-018).
|
||||
- **Dil kimliği:** prosedürel, C-ailesi sözdizimi, zorunlu class/main boilerplate
|
||||
yok. **Semantik (ADR-020):** primitive (`int`/`float`/`bool`) = **değer**;
|
||||
bileşik (`struct`/`array`/`string`) = **referans** (JS/Java/C# modeli). "Pointer
|
||||
yok" = kullanıcıya `&`/`*` **sözdizimi** verilmez; derleyici/runtime içeride ve
|
||||
ç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
|
||||
**klon** üstünde dönüşüm. `ASTNode::clone()` yük taşıyan merkezi bileşen
|
||||
(parent pointer'lar + sembol tablosu remap edilir, ADR-007). Fixpoint döngüsü +
|
||||
iterasyon tavanı (`maxFixpointRounds`, ADR-009).
|
||||
- **Literal/tip kuralı:** tamsayı literali bağlama-göre tiplenir (`float x = 1;`
|
||||
geçerli; `int y = 1.5;`→E003; değişken→değişken gizli dönüşüm yok). Döngüsel
|
||||
by-value struct → E010. (ADR-010/011)
|
||||
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`
|
||||
ilk müşteri (ADR-016). Batteries = sınır/FFI problemi, "zlib'i yeniden yaz"
|
||||
değil; kripto asla elle yazılmaz (ADR-017).
|
||||
|
|
@ -60,8 +88,11 @@ git'te izlenir.
|
|||
## Belge haritası
|
||||
- `readme.md` — toolbox çerçevesi, built-vs-planned, dil kimliği, çalıştırma modeli.
|
||||
- `docs/fikirler.md` — ADR-001…005 (backend stratejisi, parser, header-only, token, IR).
|
||||
- `docs/adr-frontend-analiz.md` — ADR-006…019 (frontend, analiz/optimizasyon,
|
||||
çalıştırma modeli, FFI, interface, bellek).
|
||||
- `docs/adr-frontend-analiz.md` — ADR-006…024 (frontend, analiz/optimizasyon,
|
||||
ç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/transkript-frontend-tasarim.md` — tasarım oturumu transkripti.
|
||||
- `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
|
||||
|
||||
**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 == "bool") return Bool();
|
||||
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();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -82,6 +82,12 @@ enum class Opcode {
|
|||
|
||||
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 ---
|
||||
// "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.
|
||||
|
|
@ -111,6 +117,10 @@ inline const char* opcodeName(Opcode op) {
|
|||
case Opcode::SHL: return "SHL";
|
||||
case Opcode::SHR: return "SHR";
|
||||
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::STORE_GLOBAL: return "STORE_GLOBAL";
|
||||
case Opcode::LESS: return "LESS";
|
||||
|
|
|
|||
|
|
@ -121,6 +121,18 @@ void IRFunction::dump() const {
|
|||
} else if (ins.opcode == Opcode::BNOT) {
|
||||
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) {
|
||||
std::cout << slot(ins.dest) << " = global[" << ins.intValue << "]";
|
||||
|
||||
|
|
|
|||
|
|
@ -376,9 +376,19 @@ int IRGenerator::generateExpression(ASTNode* node) {
|
|||
case ASTKind::BinaryExpression: {
|
||||
auto* bin = (BinaryExpressionNode*)node;
|
||||
|
||||
// Atama operatörleri: x = expr
|
||||
// Atama operatörleri: x = expr ve a[i] = expr
|
||||
if (bin->Operator == TokenType::EQUAL) {
|
||||
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;
|
||||
std::string varName = lhsId->parserToken.token->token;
|
||||
|
||||
|
|
@ -579,6 +589,30 @@ int IRGenerator::generateExpression(ASTNode* node) {
|
|||
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:
|
||||
// Bilinmeyen ifade türü
|
||||
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));
|
||||
}
|
||||
|
||||
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 {
|
||||
return nameToGlobal_.count(name) > 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -58,6 +58,10 @@ private:
|
|||
void emitLoadSlot(int destSlot, int srcSlot);
|
||||
void emitLoadGlobal(int destSlot, 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 emitReturn(int srcSlot);
|
||||
// Koşulsuz atlama yazar; instruction indeksini döndürür (backpatch için).
|
||||
|
|
|
|||
|
|
@ -107,6 +107,8 @@ enum class ASTKind {
|
|||
// children: [object], [member]
|
||||
IndexExpression, // Dizi/indeks erişimi: a[i].
|
||||
// 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();
|
||||
}
|
||||
|
||||
// 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() { kind = ASTKind::IndexExpression; }
|
||||
void IndexExpressionNode::log(int indent) {
|
||||
|
|
|
|||
|
|
@ -44,4 +44,13 @@ public:
|
|||
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
|
||||
|
|
|
|||
|
|
@ -150,6 +150,23 @@ ASTNode* Parser::parseNullDenotation() {
|
|||
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({
|
||||
TokenType::PLUS_PLUS, TokenType::MINUS_MINUS,
|
||||
TokenType::PLUS, TokenType::MINUS,
|
||||
|
|
@ -315,6 +332,13 @@ ASTNode* Parser::parseFunctionDecl() {
|
|||
if (!isTypeKw || !typeTok.token) break;
|
||||
std::string paramType = typeTok.token->token;
|
||||
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;
|
||||
VariableDeclNode* param = new VariableDeclNode();
|
||||
param->loc = currentToken().token->loc;
|
||||
|
|
@ -364,6 +388,14 @@ ASTNode* Parser::parseVariableDecl() {
|
|||
vd->varType = currentToken().token->token;
|
||||
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) {
|
||||
std::cerr << "Parser hatası: değişken ismi bekleniyor\n";
|
||||
return vd;
|
||||
|
|
@ -372,6 +404,7 @@ ASTNode* Parser::parseVariableDecl() {
|
|||
vd->name = currentToken().token->token;
|
||||
nextToken();
|
||||
|
||||
// C stili: int x[] — geriye dönük uyumluluk (postfix [])
|
||||
if (currentToken().type == TokenType::LBRACKET) {
|
||||
nextToken();
|
||||
while (currentToken().type != TokenType::RBRACKET &&
|
||||
|
|
@ -380,6 +413,7 @@ ASTNode* Parser::parseVariableDecl() {
|
|||
nextToken();
|
||||
if (currentToken().type == TokenType::RBRACKET)
|
||||
nextToken();
|
||||
if (vd->varType.back() != ']') vd->varType += "[]";
|
||||
}
|
||||
|
||||
if (currentToken().type == TokenType::EQUAL) {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
#include "vm/interpreter.hpp"
|
||||
#include "vm/object.hpp"
|
||||
#include <iostream>
|
||||
#include <stdexcept>
|
||||
|
||||
|
|
@ -121,17 +122,25 @@ int Interpreter::run() {
|
|||
break;
|
||||
case Opcode::EQUAL_EQUAL: {
|
||||
auto& lv = frame.slots[instr.left]; auto& rv = frame.slots[instr.right];
|
||||
int r = (lv.kind == ValueKind::String)
|
||||
? (lv.stringValue == rv.stringValue ? 1 : 0)
|
||||
: (lv.intValue == rv.intValue ? 1 : 0);
|
||||
int r;
|
||||
if (lv.kind == ValueKind::Ref || rv.kind == ValueKind::Ref)
|
||||
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);
|
||||
break;
|
||||
}
|
||||
case Opcode::NOT_EQUAL: {
|
||||
auto& lv = frame.slots[instr.left]; auto& rv = frame.slots[instr.right];
|
||||
int r = (lv.kind == ValueKind::String)
|
||||
? (lv.stringValue != rv.stringValue ? 1 : 0)
|
||||
: (lv.intValue != rv.intValue ? 1 : 0);
|
||||
int r;
|
||||
if (lv.kind == ValueKind::Ref || rv.kind == ValueKind::Ref)
|
||||
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);
|
||||
break;
|
||||
}
|
||||
|
|
@ -184,6 +193,48 @@ int Interpreter::run() {
|
|||
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 ───────────────────────────────────────────────────────────
|
||||
case Opcode::CALLHOST:
|
||||
executeHostFunction(instr.functionName, frame.slots, instr.argSlots);
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
#include <vector>
|
||||
#include "ir/ir_program.hpp"
|
||||
#include "vm/call_frame.hpp"
|
||||
#include "vm/object.hpp"
|
||||
|
||||
class Interpreter {
|
||||
public:
|
||||
|
|
@ -29,6 +30,7 @@ private:
|
|||
IRProgram& program_;
|
||||
std::vector<CallFrame> callStack_;
|
||||
std::vector<Value> globalSlots_;
|
||||
Heap heap_;
|
||||
|
||||
// Host (C++) fonksiyon çağrısı — şu an sadece "print" destekli
|
||||
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 <stdexcept>
|
||||
|
||||
// Çalışma zamanı değer tipi.
|
||||
//
|
||||
// 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).
|
||||
// Forward — Object tam tanımı object.hpp'de; Value onu pointer olarak taşır.
|
||||
struct Object;
|
||||
|
||||
// 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.
|
||||
enum class ValueKind {
|
||||
Int,
|
||||
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 {
|
||||
ValueKind kind = ValueKind::Int;
|
||||
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) {
|
||||
Value v;
|
||||
v.kind = ValueKind::Int;
|
||||
v.intValue = n;
|
||||
return v;
|
||||
Value v; v.kind = ValueKind::Int; v.intValue = n; return v;
|
||||
}
|
||||
|
||||
static Value fromString(std::string s) {
|
||||
Value v;
|
||||
v.kind = ValueKind::String;
|
||||
v.stringValue = std::move(s);
|
||||
return v;
|
||||
Value 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 {
|
||||
switch (kind) {
|
||||
case ValueKind::Int: return intValue != 0;
|
||||
case ValueKind::String: return !stringValue.empty();
|
||||
case ValueKind::Ref: return ref != nullptr;
|
||||
case ValueKind::Nil: return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Yazdırma ve hata mesajları için okunabilir temsil
|
||||
std::string toString() const {
|
||||
switch (kind) {
|
||||
case ValueKind::Int: return std::to_string(intValue);
|
||||
case ValueKind::String: return stringValue;
|
||||
case ValueKind::Ref: return "<array>";
|
||||
case ValueKind::Nil: return "nil";
|
||||
}
|
||||
return "?";
|
||||
}
|
||||
|
||||
// Tip adı — hata mesajları için
|
||||
std::string typeName() const {
|
||||
switch (kind) {
|
||||
case ValueKind::Int: return "int";
|
||||
case ValueKind::String: return "string";
|
||||
case ValueKind::Ref: return "array";
|
||||
case ValueKind::Nil: return "nil";
|
||||
}
|
||||
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