initial commit

This commit is contained in:
2026-04-05 16:08:59 +03:00
commit 75ab9bec9f
1117 changed files with 789034 additions and 0 deletions

91
docs/architecture.md Normal file
View File

@@ -0,0 +1,91 @@
# Genel Mimari
dexpr, kaynak kodu parse edip bytecode'a derleyen ve register tabanlı bir VM üzerinde çalıştıran bir ifade değerlendirici (expression evaluator) ve bytecode VM'dir.
---
## Pipeline
```
Kaynak Kod (.dexpr)
[PARSER] ← PEG tabanlı gramer kuralları
AST (Expr, Stmt, Value)
[COMPILER] ← Tek geçişli derleme
Bytecode (ham byte dizisi)
[VM] ← 8 register, global'ler
Sonuç (veya kaynak konum bilgili hata)
```
---
## Temel Tasarım Kararları
1. **Register tabanlı VM:** Stack tabanlı VM'lere göre daha az komut, daha hızlı çalışma
2. **8 register limiti:** Basitlik ve hız arasında denge
3. **Tek geçişli derleme:** Fonksiyon desteği kaldırıldığı için iki geçişe gerek yok
4. **Label tabanlı atlamalar:** Son geçişte çözümlenir
5. **Span izleme:** Derleme ve çalışma zamanı hataları kaynak kod konumunu gösterir
6. **Value serileştirme:** Sabitler doğrudan bytecode'a gömülür
7. **Object tipi:** `IndexMap<SmolStr, Value>` tabanlı anahtar-değer nesneleri; özellik erişimi (`obj.field`), iç içe atama (`a.b.c = expr`) ve built-in metodlar (`keys`, `values`, `contains`, `get`, `length`) desteklenir
8. **Sadece global değişkenler:** Tüm değişkenler global scope'ta; host uygulamadan `set_global` ile değer aktarılır
9. **Bytecode cache:** Compile edilmiş bytecode saklanıp farklı global değerlerle tekrar çalıştırılabilir
10. **Harici fonksiyonlar:** Host fonksiyonları VM'de isimle kayıt edilir, runtime'da HashMap lookup ile çözümlenir
11. **Harici metodlar:** Tipe özel host metodları eklenebilir, built-in metodlar bulunamazsa kontrol edilir
12. **Expression return:** `execute()` son ExprStmt'ın değerini döndürür — tek satır formüller doğrudan sonuç verir
---
## Tipik Kullanım
```rust
use dexpr::{ast::value::Value, compiler::Compiler, parser, vm::VM};
// 1. Parse
let ast = parser::program(source_code)?;
// 2. Compile
let mut compiler = Compiler::new();
let bytecode = compiler.compile(ast)?;
// 3. Execute (bytecode cache'lenip tekrar kullanılabilir)
let mut vm = VM::new(&bytecode);
vm.set_global("input", Value::Number(dec!(42)));
vm.register_function("getRate", |args| { Ok(Value::Number(dec!(34.5))) });
let result = vm.execute()?; // Son expression'ın değerini döndürür
// veya: let output = vm.get_global("output");
```
---
## Bağımlılıklar
| Crate | Kullanım |
|-------|----------|
| `rust_decimal` | Keyfi hassasiyetli ondalık aritmetik |
| `peg` | PEG tabanlı parser üreteci |
| `smol_str` | Kısa string'ler için optimize edilmiş tip |
| `micromap` | Kompakt hashmap (global değişkenler için) |
| `bumpalo` | Bump allocator (hızlı bellek ayırma) |
| `thiserror` | Hata türleri için derive makrosu |
| `rustc-hash` | Hızlı hash fonksiyonu |
| `smallvec` | Stack-allocated vektörler |
| `indexmap` | Sıralı hashmap (Object tipi için) |
---
## Modül Referansları
- [AST Modülü](ast.md) — İfadeler, deyimler, değer türleri
- [Parser Modülü](parser.md) — Gramer kuralları, ayrıştırma
- [Opcodes Modülü](opcodes.md) — Bytecode komut seti
- [Bytecode Modülü](bytecode.md) — Serileştirme, disassembler
- [Compiler Modülü](compiler.md) — AST → bytecode dönüşümü
- [VM Modülü](vm.md) — Sanal makine, çalıştırma
- [Language Info Modülü](language_info.md) — Editör metadata üretimi
- [Editor Modülü](editor.md) — CodeMirror 6 dil desteği (codemirror-lang-dexpr)

115
docs/ast.md Normal file
View File

@@ -0,0 +1,115 @@
# AST (Abstract Syntax Tree) Modülü
**Konum:** `src/ast/`
AST modülü, parser tarafından üretilen ve compiler tarafından tüketilen ağaç yapısını tanımlar. Üç alt modülden oluşur: `expr.rs`, `stmt.rs`, `value.rs`.
---
## Span & Spanned
**Dosya:** `src/ast/expr.rs`
`Span`, kaynak koddaki bir konumu (satır, sütun) temsil eder. 1-indexed'tir.
```rust
struct Span { line: u32, column: u32 }
```
`Spanned<T>`, herhangi bir AST node'unu kaynak kod konumuyla eşleştirir:
```rust
struct Spanned<T> { node: T, span: Span }
```
`dummy()` metodu test ve internal kullanım için varsayılan konum oluşturur.
---
## Expr (İfadeler)
**Dosya:** `src/ast/expr.rs`
**Type alias:** `SpannedExpr = Spanned<Expr>`
`Expr` enum'u tüm ifade türlerini kapsar:
| Variant | Açıklama | Örnek |
|---------|----------|-------|
| `Value(Value)` | Sabit değer (literal) | `42`, `"hello"`, `true` |
| `Variable(SmolStr)` | Değişken referansı | `x`, `result` |
| `BinaryOp(Box<Expr>, Op, Box<Expr>)` | İkili operasyon | `a + b`, `x > 5` |
| `UnaryOp(Op, Box<Expr>)` | Tekli operasyon | `-x`, `!flag` |
| `FunctionCall(SmolStr, Vec<Expr>)` | Built-in fonksiyon çağrısı | `log(x)` |
| `MethodCall(Box<Expr>, SmolStr, Vec<Expr>)` | Metod çağrısı | `name.upper()`, `text.split(",")` |
| `PropertyAccess(Box<Expr>, SmolStr)` | Özellik erişimi | `obj.field`, `person.name` |
> **Not:** `FunctionCall` hem built-in (`log`) hem de harici (host) fonksiyonlar için kullanılır. Built-in olmayan fonksiyonlar `CallExternal` opcode'u ile derlenir ve VM tarafından runtime'da çözümlenir.
---
## Op (Operatörler)
**Dosya:** `src/ast/expr.rs`
| Kategori | Operatörler |
|----------|-------------|
| Aritmetik | `Add`, `Sub`, `Mul`, `Div`, `Mod`, `Pow` |
| Karşılaştırma | `Lt`, `Lte`, `Gt`, `Gte`, `Eq`, `Neq` |
| Boolean | `And`, `Or`, `Not` |
| Üyelik | `In` (değer listede/string'de var mı) |
| Tekli | `Neg` (negatif) |
---
## Stmt (İfadeler/Deyimler)
**Dosya:** `src/ast/stmt.rs`
**Type alias:** `SpannedStmt = Spanned<Stmt>`
| Variant | Açıklama | Sözdizimi |
|---------|----------|-----------|
| `Assignment(SmolStr, Box<Expr>)` | Değişken ataması | `x = 5` |
| `ExprStmt(Box<Expr>)` | İfade deyimi | `log(x)` |
| `If(Box<Expr>, Vec<Stmt>, Option<Vec<Stmt>>)` | Koşullu deyim | `if x > 0 then ... end` |
| `PropertyAssignment(SmolStr, Vec<SmolStr>, Box<Expr>)` | Özellik ataması | `a.b.c = expr` |
---
## Value (Çalışma Zamanı Değerleri)
**Dosya:** `src/ast/value.rs`
| Variant | Rust Tipi | Açıklama |
|---------|-----------|----------|
| `Null` | - | Boş değer |
| `Number(Decimal)` | `rust_decimal::Decimal` | Keyfi hassasiyetli ondalık sayı |
| `String(SmolStr)` | `smol_str::SmolStr` | Optimize edilmiş string |
| `Boolean(bool)` | `bool` | Mantıksal değer |
| `NumberList(Vec<Decimal>)` | `Vec<Decimal>` | Sayı listesi |
| `StringList(Vec<SmolStr>)` | `Vec<SmolStr>` | String listesi |
| `Object(IndexMap<SmolStr, Value>)` | `IndexMap<SmolStr, Value>` | Anahtar-değer nesnesi |
### Serileştirme
Her `Value` bytecode'a gömülebilir. Serileştirme formatı:
1. **Tip etiketi** (1 byte): `NULL=0x00`, `NUMBER=0x01`, `STRING=0x02`, `BOOLEAN=0x03`, `NUMBER_LIST=0x04`, `STRING_LIST=0x05`, `OBJECT=0x06`
2. **Veri:**
- Number: 16 byte (Decimal serialization)
- String: 2-byte uzunluk + UTF-8 bytes
- Boolean: 1 byte (0 veya 1)
- NumberList: 2-byte count + her sayı için 16 byte
- StringList: 2-byte count + her string için (2-byte uzunluk + bytes)
- Object: 2-byte entry count + her girdi için (anahtar: 2-byte uzunluk + bytes, değer: rekürsif serialize)
`serialize()` ve `deserialize()` metodları bu dönüşümü gerçekleştirir.
---
## Modüller Arası İlişki
```
Parser --> Expr, Stmt, Value (AST üretir)
Compiler <-- Expr, Stmt, Value (AST tüketir, bytecode üretir)
VM <-- Value (çalışma zamanında değer olarak kullanılır)
```

79
docs/bytecode.md Normal file
View File

@@ -0,0 +1,79 @@
# Bytecode Modülü
**Konum:** `src/bytecode.rs`
Bytecode'un serileştirilmesi (yazma) ve deserileştirilmesi (okuma) işlemlerini sağlar. Compiler bytecode üretirken `BytecodeWriter`'ı, VM çalıştırırken `BytecodeReader`'ı kullanır.
---
## BytecodeWriter
Compiler tarafından bytecode üretmek için kullanılır.
| Metod | Açıklama |
|-------|----------|
| `new()` | Boş writer oluştur |
| `write_byte(u8)` | Tek byte yaz |
| `write_u16(u16)` | Big-endian 16-bit tamsayı yaz |
| `write_u32(u32)` | Big-endian 32-bit tamsayı yaz |
| `write_register(u8)` | Register indeksi yaz |
| `write_string(SmolStr)` | String yaz (2-byte uzunluk + UTF-8) |
| `write_value(Value)` | Serileştirilmiş Value yaz |
| `position() -> usize` | Geçerli bytecode pozisyonu |
| `bytecode() -> &[u8]` | Bytecode'un immutable view'ı |
| `into_bytecode() -> Vec<u8>` | Writer'ı tüket, byte vektörü döndür |
---
## BytecodeReader
VM tarafından bytecode'u okumak için kullanılır.
| Metod | Açıklama |
|-------|----------|
| `new(bytecode)` | Bytecode'dan reader oluştur |
| `read_byte() -> Result<u8>` | Tek byte oku |
| `read_u16() -> Result<u16>` | Big-endian 16-bit tamsayı oku |
| `read_u32() -> Result<u32>` | Big-endian 32-bit tamsayı oku |
| `read_register() -> Result<u8>` | Register indeksi oku |
| `read_string() -> Result<SmolStr>` | String oku |
| `read_value() -> Result<Value>` | Value deserileştir ve oku |
| `position() -> usize` | Geçerli okuma pozisyonu |
| `set_position(usize)` | Pozisyona atla (bounds check ile) |
| `remaining() -> usize` | Kalan byte sayısı |
---
## Veri Formatı
Tüm çok-byte değerler **big-endian** formatında saklanır.
### String Formatı
```
[2 bytes: uzunluk (u16)] [N bytes: UTF-8 veri]
```
### Value Formatı
```
[1 byte: tip etiketi] [N bytes: tipe göre veri]
```
Detaylı Value serileştirme formatı için [AST dokümantasyonuna](ast.md#serileştirme) bakın.
---
## Bytecode Dump (Disassembler)
**Konum:** `src/bytecode_dump.rs`
`disassemble_bytecode(bytecode) -> Vec<String>` fonksiyonu bytecode'u insan okunabilir formata çevirir:
```
0000: LoadConst r0, 42
0008: StoreGlobal "x", r0
000d: LoadGlobal r0, "x"
0014: Log r0
0016: End
```
Bu araç debug ve geliştirme sürecinde bytecode'u incelemek için kullanılır.

150
docs/compiler.md Normal file
View File

@@ -0,0 +1,150 @@
# Compiler Modülü
**Konum:** `src/compiler.rs`
AST'yi bytecode'a dönüştürür. Tek geçişli (single-pass) derleme yapar. Tüm değişkenler global scope'tadır.
---
## Konfigürasyon
- `MAX_REGISTERS = 8` — Hesaplama için kullanılabilir register sayısı
---
## Hata Türleri (CompileError)
| Hata | Açıklama |
|------|----------|
| `UndefinedFunction(SmolStr)` | Tanımlanmamış fonksiyon (built-in dışı çağrı) |
| `RegisterLimitExceeded` | Register limiti aşıldı |
| `InvalidExpression(String)` | Geçersiz ifade |
| `InvalidStatement(String)` | Geçersiz deyim |
| `BytecodeError(String)` | Bytecode üretim hatası |
---
## Compiler Yapısı
```rust
struct Compiler {
writer: BytecodeWriter,
used_registers: Vec<bool>,
// Jump address resolution
pending_jumps: Vec<(usize, usize)>,
labels: HashMap<usize, usize>,
next_label: usize,
// Debug info generation
debug_info: DebugInfo,
current_span: Span,
}
```
---
## Derleme Akışı
### Ana Derleme: `compile(statements) -> Vec<u8>`
Tek geçişli derleme süreci:
```
1. Deyimleri sırayla derle
2. End opcode'u yaz
3. Atlama adreslerini çözümle (resolve_jumps)
```
### Kaynak Koddan Derleme: `compile_from_source(source) -> (Vec<u8>, DebugInfo)`
Parse ile birlikte pozisyon bilgisi de toplar ve `DebugInfo` üretir.
---
## Deyim Derleme
### Assignment (Atama)
1. İfadeyi register'a derle
2. `StoreGlobal` emit et (tüm değişkenler global)
### If Statement (Koşullu Deyim)
```
1. Koşulu register'a derle
2. Else ve end için label oluştur
3. JumpIfFalse → else label
4. Then dalını derle
5. Jump → end label
6. Else label'ını set et, else dalını derle
7. End label'ını set et
```
---
## İfade Derleme
### Value (Sabit Değer)
- Register ayır → `LoadConst` emit et
### Variable (Değişken)
- Register ayır → `LoadGlobal` emit et
### BinaryOp (İkili Operasyon)
1. Sol operandı register'a derle
2. Sağ operandı register'a derle
3. Sonuç register'ı ayır
4. Uygun opcode'u emit et (Add, Sub, Mul, vs.)
5. **Özel durum:** String + String → `Concat` kullanılır
6. Operand register'ları serbest bırak
### UnaryOp (Tekli Operasyon)
1. Operandı register'a derle
2. Sonuç register'ı ayır
3. `Neg` veya `Not` emit et
### FunctionCall (Fonksiyon Çağrısı)
- `log` built-in fonksiyonu: Argümanları derle → `Log` emit et → Null register döndür
- Diğer fonksiyonlar: `CallExternal` opcode emit edilir (VM tarafından runtime'da çözümlenir)
### ExprStmt (İfade Deyimi)
- İfadeyi derle → `SetResult` emit et → Register'ı serbest bırak
- `SetResult`, VM'in `execute()` dönüş değerini belirler (son ExprStmt kazanır)
### MethodCall (Metod Çağrısı)
- Nesneyi register'a derle
- Argümanları derle
- `MethodCall` emit et (sonuç, nesne, metod adı, argüman sayısı, argüman register'ları)
### PropertyAccess (Özellik Erişimi)
- Nesneyi register'a derle
- `GetProperty` emit et (hedef register, nesne register, özellik adı string)
### PropertyAssignment (Özellik Ataması)
- İç içe özellik zinciri (`a.b.c = expr`) için:
1. Kök değişkeni `LoadGlobal` ile yükle
2. Ara özellikler için `GetProperty` zinciri emit et
3. Son özellik için `SetProperty` emit et
4. Değiştirilmiş kök nesneyi `StoreGlobal` ile geri yaz
---
## Register Yönetimi
- `allocate_register() -> u8` — İlk boş register'ı bul, yoksa hata
- `free_register(reg)` — Register'ı kullanılabilir olarak işaretle
- Toplam 8 register limiti var
---
## Label ve Jump Yönetimi
| Metod | Açıklama |
|-------|----------|
| `create_label() -> usize` | Benzersiz label ID üret |
| `set_label(id)` | Label'ın bytecode pozisyonunu kaydet |
| `emit_jump_address(label) -> usize` | Placeholder yaz, çözümleme için kaydet |
| `emit_jump(label)` | Koşulsuz atlama emit et |
| `resolve_jumps()` | Tüm placeholder'ları gerçek adreslerle doldur |

266
docs/editor.md Normal file
View File

@@ -0,0 +1,266 @@
# Editor Modülü (codemirror-lang-dexpr)
**Konum:** `editor/`
CodeMirror 6 için dexpr dil desteği kütüphanesi. Syntax highlighting, tip-bazlı autocomplete ve error-tolerant parsing sağlar.
---
## Mimari
```
editor/
src/
dexpr.grammar ← Lezer gramer dosyası (kaynak)
parser.js ← Lezer tarafından üretilen parser (generated)
parser.terms.js ← Token tanımları (generated)
tokens.ts ← External tokenizer (else if → tek token)
language.ts ← LRLanguage tanımı + syntax highlighting tag'leri
highlight.ts ← Varsayılan renk teması
completions.ts ← Autocomplete: tip çıkarımı + metadata bazlı öneriler
index.ts ← Ana export: dexpr() fonksiyonu
demo.ts ← Test sayfası kaynak kodu
demo.html ← Test sayfası
dexpr.grammar ← (src altındaki asıl dosya)
```
### İki Parser Stratejisi
| Taraf | Parser | Amaç |
|-------|--------|------|
| **Rust** (execution) | PEG (`peg` crate) | AST üretimi → derleme → VM çalıştırma |
| **Editor** (web) | Lezer | Syntax highlighting, error recovery, autocomplete |
Dil küçük olduğundan (7 keyword, ~20 operatör) iki gramer dosyasını sync tutmak kolaydır.
---
## Lezer Grammar
**Dosya:** `src/dexpr.grammar`
### Parse Kuralları
| Kural | Açıklama |
|-------|----------|
| `Program` | `statement*` — üst düzey kural |
| `IfStatement` | `if expr then stmts (else if expr then stmts)* (else stmts)? end` |
| `Assignment` | `VariableName AssignOp expression` veya `VariableName (.PropertyName)+ AssignOp expression` |
| `PropertyAccess` | `expression.PropertyName` — nesne özellik erişimi |
| `ExprStatement` | Bağımsız ifade |
| `BinaryExpression` | İkili operatörler, öncelik sırasıyla |
| `UnaryExpression` | Tekli `-` ve `!` |
| `MethodCall` | `expression.PropertyName(args)` |
| `FunctionCall` | `VariableName(args)` |
| `ParenExpression` | `(expression)` |
### Operatör Önceliği (düşükten yükseğe)
1. `||` — mantıksal OR
2. `&&` — mantıksal AND
3. `==`, `!=`, `<`, `<=`, `>`, `>=`, `in` — karşılaştırma
4. `+`, `-` — toplama/çıkarma
5. `*`, `/`, `%` — çarpma/bölme/mod
6. `**` — üs alma (sağdan birleşimli)
7. `-`, `!` — tekli operatörler (prefix)
8. `.method()` — metod çağrısı
9. `name()` — fonksiyon çağrısı
### External Tokenizer
**Dosya:** `src/tokens.ts`
`else if` iki ayrı keyword olarak yazılır ama Lezer'da tek token olarak tanınır (`elseIf`). External tokenizer `else` + boşluk + `if` dizisini tespit edip tek token üretir. Bu sayede `else if` zinciri ile nested `if` arasındaki belirsizlik (ambiguity) ortadan kalkar.
### Keyword Yönetimi
Keyword'ler `@extend` ile tanımlanır — `identifier` token'ından türetilir ama **her zaman** keyword olarak parse edilir. dexpr'de keyword'ler reserved'dır (`if`, `then`, `else`, `end`, `in`, `true`, `false`).
### Error Recovery
Lezer GLR parser kullanır. Bozuk/yarım kod yazılırken:
- Parse edilebilen kısımlar doğru tree node'ları üretir
- Hatalı kısımlar `⚠` (error) node'ları ile sarılır
- Editör yarım kodda bile syntax highlighting ve autocomplete sunabilir
---
## Syntax Highlighting
**Dosya:** `src/language.ts` (tag eşleştirme) + `src/highlight.ts` (renk teması)
### Token → Tag Eşleştirmesi
| Token | Lezer Tag | Varsayılan Renk |
|-------|-----------|-----------------|
| `if`, `then`, `else`, `end`, `in`, `elseIf` | `keyword` | `#7c3aed` (mor) |
| `true`, `false` | `bool` | `#d97706` (turuncu) |
| `"string"`, `'string'` | `string` | `#059669` (yeşil) |
| `42`, `3.14` | `number` | `#2563eb` (mavi) |
| `// comment`, `/* comment */` | `lineComment` / `blockComment` | `#9ca3af` (gri, italic) |
| `+`, `-`, `*`, `/`, `%`, `!`, `\|\|`, `&&` | `operator` | `#dc2626` (kırmızı) |
| `==`, `!=`, `<`, `<=`, `>`, `>=`, `=`, `**` | `compareOperator` | `#dc2626` (kırmızı) |
| `myVar` | `variableName` | `#1f2937` (koyu) |
| `.method` | `propertyName` | `#0891b2` (cyan) |
| `functionName()` | `function(variableName)` | `#9333ea` (mor) |
Host uygulama `highlighting: false` vererek varsayılan temayı devre dışı bırakıp kendi temasını kullanabilir.
---
## Autocomplete
**Dosya:** `src/completions.ts`
### Veri Kaynağı
Autocomplete verileri `DexprLanguageInfo` arayüzü ile sağlanır. Bu veri Rust tarafında `LanguageInfo::to_json()` ile üretilir. Detaylar: [Language Info Modülü](language_info.md)
```typescript
interface DexprLanguageInfo {
functions: FunctionInfo[];
methods: Partial<Record<DexprType, MethodInfo[]>>;
variables?: VariableInfo[];
}
```
### Tip Çıkarımı (Type Inference)
Completions modülü Lezer syntax tree'sini kullanarak değişken tiplerini çıkarır:
1. **Config'den gelen tipler:** `variables` dizisindeki her değişkenin tipi bilinir
2. **Assignment analizi:** `x = "hello"``x: String`, `y = 42``y: Number`
3. **Method dönüş tipi:** `z = name.split(",")``z: StringList` (`split` dönüş tipi bilinir)
4. **Binary expression:** `a = x + y` → string varsa `String`, number varsa `Number`
Bu analiz her autocomplete tetiklendiğinde Lezer tree üzerinde yapılır. Bozuk kodda bile parse edilmiş assignment'lar doğru tip bilgisi verir.
### Tetikleme Kuralları
| Durum | Davranış |
|-------|----------|
| `identifier` yazılırken | Keyword, fonksiyon, değişken önerileri |
| `.` yazıldığında | Dot öncesi ifadenin tipine göre metod önerileri |
| `.` + tip bilinmiyor | Tüm metodlar (fallback) |
| `.` + `Number` tipi | Öneri yok (ondalık yazımıyla karışmaz) |
| String / comment içinde | Öneri yok |
| Ctrl+Space | Explicit tetikleme |
### Metod Önerileri (tipe göre)
| Dot Öncesi | Gösterilen Metodlar |
|------------|---------------------|
| `"hello".` | String metodları |
| `category.` (config'de `String`) | String metodları |
| `x.` (assignment'tan `String` çıkarıldı) | String metodları |
| `items.` (config'de `StringList`) | StringList metodları |
| `scores.` (config'de `NumberList`) | NumberList metodları |
| `obj.` (config'de `Object`) | Object metodları |
| `result.` (tip bilinmiyor) | Tüm metodlar |
| `42.` | Öneri yok |
---
## Kurulum ve Build
```bash
cd editor
# Bağımlılıkları kur
bun install
# Lezer parser'ı grammar'dan üret
npx lezer-generator src/dexpr.grammar -o src/parser.js
# Kütüphaneyi build et
bun run build # → dist/index.js, dist/index.cjs, dist/index.d.ts
# Demo'yu build et (test için)
bun run demo # → dist/demo.global.js
# Demo'yu çalıştır
bunx serve . -p 3456 # → http://localhost:3456/demo.html
```
### Grammar Değişikliği Yapıldığında
1. `src/dexpr.grammar` dosyasını düzenle
2. `npx lezer-generator src/dexpr.grammar -o src/parser.js` çalıştır
3. `bun run build` ile yeniden derle
---
## Host Uygulama Entegrasyonu
### 1. Rust Tarafı: Metadata Üretimi
```rust
use dexpr::language_info::LanguageInfo;
let mut info = LanguageInfo::builtin();
// VM'de register edilen her fonksiyon için:
info.add_function("getRate", "(code: String) -> Number", Some("Kur bilgisi"));
// VM'de register edilen her metod için:
info.add_method("String", "toTitleCase", "() -> String", None);
// VM'de set_global ile verilen her değişken için:
info.add_variable("price", "Number", None);
info.add_variable("category", "String", None);
let json = info.to_json();
```
### 2. Frontend Tarafı: Editör Oluşturma
```typescript
import { EditorView, basicSetup } from "codemirror";
import { EditorState } from "@codemirror/state";
import { dexpr } from "codemirror-lang-dexpr";
// Rust'tan gelen JSON
const languageInfo = JSON.parse(jsonFromRust);
new EditorView({
state: EditorState.create({
doc: "",
extensions: [basicSetup, dexpr(languageInfo)],
}),
parent: document.getElementById("editor")!,
});
```
### 3. Dinamik Güncelleme
Eğer host uygulama çalışma sırasında yeni fonksiyon/değişken eklerse, editörü yeni `languageInfo` ile yeniden oluşturmak gerekir. CodeMirror'un `EditorView.dispatch` ile extension'ları güncellemek mümkündür ama en basit yol editörü yeniden oluşturmaktır.
---
## Export'lar
### Ana Export
| Export | Tip | Açıklama |
|--------|-----|----------|
| `dexpr(config)` | `Extension` | All-in-one: language + autocomplete + highlighting |
### Granüler Export'lar
| Export | Açıklama |
|--------|----------|
| `dexprLanguage` | Sadece `LRLanguage` tanımı |
| `dexprCompletion(info)` | Sadece autocomplete extension'ı |
| `dexprHighlighting()` | Sadece varsayılan renk teması |
| `dexprHighlightStyle` | `HighlightStyle` nesnesi (özelleştirme için) |
| `KEYWORDS` | Keyword completion listesi |
### Tip Export'ları
| Tip | Açıklama |
|-----|----------|
| `DexprLanguageInfo` | Metadata arayüzü (JSON yapısı) |
| `DexprType` | `"String" \| "Number" \| "Boolean" \| "NumberList" \| "StringList" \| "Object"` |
| `FunctionInfo` | Fonksiyon metadata'sı |
| `MethodInfo` | Metod metadata'sı |
| `VariableInfo` | Değişken metadata'sı |

294
docs/embedding.md Normal file
View File

@@ -0,0 +1,294 @@
# dexpr Embedding Guide
dexpr'i başka projelere embed etmenin iki yolu var:
1. **Rust backend + Web frontend** — Rust tarafında çalıştırma, frontend'de editör
2. **Tamamen tarayıcıda (WASM)** — Hem çalıştırma hem editör tarayıcıda
---
## Yol 1: Rust Backend + Web Frontend
Tipik senaryo: kullanıcı editörde formül yazar, backend derler ve çalıştırır.
### Rust tarafı
```toml
# Cargo.toml
[dependencies]
dexpr = { path = "../dexpr" } # veya git/crates.io
rust_decimal_macros = "1.40"
indexmap = "2"
smol_str = "0.3"
```
```rust
use dexpr::{ast::value::Value, compiler::Compiler, vm::VM, language_info::LanguageInfo};
// ── 1. Verileri hazırla ──
// JSON'dan (API, DB, config, vb.)
let customer = Value::from_json(r#"{
"name": "Alice",
"email": "alice@test.com",
"age": 30,
"active": true,
"tags": ["premium", "tr"]
}"#).unwrap();
let order = Value::from_json(r#"{
"amount": 1500,
"currency": "TRY",
"items": ["laptop", "mouse"]
}"#).unwrap();
// ── 2. Editör metadata'sını üret (sayfa yüklenirken, 1 kez) ──
let mut info = LanguageInfo::builtin();
// Değişkenler — tip ve field bilgisi Value'dan otomatik çıkar
info.add_value("customer", &customer, Some("Müşteri bilgisi".into()));
info.add_value("order", &order, Some("Sipariş bilgisi".into()));
info.add_value("discount", &Value::Number(rust_decimal_macros::dec!(10)), None);
// Host fonksiyonları
info.add_function("getRate", "(code: String) -> Number", Some("Döviz kuru"));
info.add_function("fetchPrice", "(sku: String) -> Number", None);
let editor_json = info.to_json();
// → bu JSON'ı bir endpoint ile frontend'e gönder
// ── 3. Kullanıcının yazdığı kodu çalıştır ──
fn execute_dexpr(
source: &str,
globals: &[(&str, Value)],
) -> Result<Value, String> {
let mut compiler = Compiler::new();
let (bytecode, debug_info) = compiler
.compile_from_source(source)
.map_err(|e| e.to_string())?;
let mut vm = VM::new(&bytecode);
vm.set_debug_info(&debug_info);
for (name, value) in globals {
vm.set_global(name, value.clone());
}
// Host fonksiyonları kaydet
vm.register_function("getRate", |args| {
// args[0]: currency code
Ok(Value::Number(rust_decimal_macros::dec!(34.5)))
});
vm.execute().map_err(|e| e.to_string())
}
// Kullanıcının formülü:
let result = execute_dexpr(
"order.amount * (1 - discount / 100)",
&[
("customer", customer),
("order", order),
("discount", Value::Number(rust_decimal_macros::dec!(10))),
],
);
// → Ok(Number(1350))
```
### Frontend tarafı
```bash
npm install codemirror codemirror-lang-dexpr
```
```typescript
import { EditorView, basicSetup } from "codemirror";
import { EditorState } from "@codemirror/state";
import { dexpr } from "codemirror-lang-dexpr";
// ── 1. Backend'den metadata al ──
const langInfo = await fetch("/api/editor-metadata").then(r => r.json());
// ── 2. Editörü oluştur ──
const editor = new EditorView({
state: EditorState.create({
doc: "",
extensions: [basicSetup, dexpr(langInfo)],
}),
parent: document.getElementById("editor")!,
});
// ── 3. Çalıştır ──
async function run() {
const code = editor.state.doc.toString();
const res = await fetch("/api/execute", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ code }),
});
const { result, error } = await res.json();
document.getElementById("output")!.textContent =
error ?? JSON.stringify(result);
}
```
### Akış
```
Frontend Backend (Rust)
─────── ──────────────
GET /editor-metadata ──────────► LanguageInfo::to_json()
◄─────────────────────────────── JSON (functions, methods, variables+fields)
dexpr(langInfo)
customer. → [name, email, age, ...]
customer.name. → [upper, lower, trim, ...]
POST /execute { code } ────────► Compiler::compile_from_source()
VM::new() + set_global() + execute()
◄─────────────────────────────── { result: 1350 }
```
---
## Yol 2: Tamamen Tarayıcıda (WASM)
Backend'e gerek yok. Parser, compiler, VM hepsi tarayıcıda çalışır.
### Build
```bash
cd wasm
wasm-pack build --target web --release
# → wasm/pkg/ altında JS + WASM + TypeScript types üretilir
```
`pkg/` içeriği:
- `dexpr_wasm_bg.wasm` (~370KB) — WASM binary
- `dexpr_wasm.js` — JS glue code (auto-init)
- `dexpr_wasm.d.ts` — TypeScript type definitions
### Kullanım
```html
<div id="editor"></div>
<button onclick="run()">Çalıştır</button>
<pre id="output"></pre>
<script type="module">
import init, { DexprEngine } from "./pkg/dexpr_wasm.js";
import { EditorView, basicSetup } from "codemirror";
import { EditorState } from "@codemirror/state";
import { dexpr } from "codemirror-lang-dexpr";
// ── 1. WASM'ı başlat ──
await init();
const engine = new DexprEngine();
// ── 2. Global'leri JSON string olarak ver ──
engine.setGlobal("customer", JSON.stringify({
name: "Alice",
email: "alice@test.com",
age: 30,
active: true,
tags: ["premium", "tr"],
}));
engine.setGlobal("order", JSON.stringify({
amount: 1500,
currency: "TRY",
items: ["laptop", "mouse"],
}));
engine.setGlobal("discount", "10");
// ── 3. Host fonksiyon kaydet ──
engine.registerFunction("getRate", (argsJson) => {
const args = JSON.parse(argsJson);
const code = args[0]; // currency code string
const rates = { USD: 34.5, EUR: 37.2 };
return JSON.stringify(rates[code] ?? 0);
});
engine.addFunctionInfo("getRate", "(code: String) -> Number", "Döviz kuru");
// ── 4. Editör metadata'sını engine'den al ──
// setGlobal çağrıları otomatik olarak field tiplerini algılar
const langInfo = JSON.parse(engine.languageInfo());
// ── 5. CodeMirror editörünü oluştur ──
const editor = new EditorView({
state: EditorState.create({
doc: `// customer ve order WASM engine'e JSON olarak verildi
name = customer.name.upper()
total = order.amount * (1 - discount / 100)
log(name, total)
total`,
extensions: [basicSetup, dexpr(langInfo)],
}),
parent: document.getElementById("editor"),
});
// ── 6. Çalıştır butonu ──
window.run = function() {
try {
const code = editor.state.doc.toString();
const resultJson = engine.execute(code);
const result = JSON.parse(resultJson);
document.getElementById("output").textContent = JSON.stringify(result, null, 2);
// Global'ler güncellendi mi kontrol et
const customer = JSON.parse(engine.getGlobal("customer"));
console.log("customer after:", customer);
} catch (e) {
document.getElementById("output").textContent = "Error: " + e.message;
}
};
</script>
```
### API Referansı
```typescript
class DexprEngine {
constructor()
// Global değişken: JSON string olarak ver/al
setGlobal(name: string, json: string): void
getGlobal(name: string): string | undefined
// Çalıştır: kaynak kodu → sonuç JSON string
execute(source: string): string
// Host fonksiyonu kaydet (args ve return JSON string olarak)
registerFunction(name: string, fn: (argsJson: string) => string): void
// Editör metadata (otomatik güncellenir her setGlobal'de)
languageInfo(): string
// Editör autocomplete için fonksiyon bilgisi
addFunctionInfo(name: string, signature: string, doc?: string): void
}
```
### Dikkat Edilmesi Gerekenler
- **JSON string convention**: `setGlobal` ve `execute` JSON *string* alır/verir — `JSON.stringify()` ve `JSON.parse()` kullan
- **Global sync**: `execute()` sonrası property assignment'lar (`customer.city = "Ankara"`) global'lere yansır — `getGlobal()` ile güncel halini al
- **Host fonksiyonlar**: Args ve return JSON string — `(argsJson: string) => string`
- **Editör metadata**: `setGlobal()` her çağrıldığında `languageInfo()` otomatik güncellenir (field tipleri Value'dan çıkarılır)
- **WASM boyutu**: Release build ~370KB (gzip ile ~120KB)
---
## Karşılaştırma
| | Rust Backend | WASM |
|---|---|---|
| **Çalıştırma** | Server'da | Tarayıcıda |
| **Performans** | Native hız | ~2-3x native |
| **Güvenlik** | Server-side validation | Client-side (güvenilir ortam) |
| **Latency** | Network roundtrip | Anında |
| **Deployment** | Backend deploy | Static dosya |
| **Host fonksiyonlar** | Rust closure'ları | JS callback (JSON bridge) |
| **DB/API erişimi** | Doğrudan | Fetch ile |
**Öneri**: Güvenlik kritikse (fiyat hesaplama, kurallar) → Rust backend. İnteraktif preview/sandbox → WASM.

156
docs/language_info.md Normal file
View File

@@ -0,0 +1,156 @@
# Language Info Modülü
**Konum:** `src/language_info.rs`
Editör entegrasyonu için dil metadata'sı üretir. Built-in fonksiyonlar, tipe göre metodlar ve host-kayıtlı genişletmeleri JSON formatında dışa aktarır. Frontend editör kütüphanesi (`codemirror-lang-dexpr`) bu JSON'u alarak tip-bazlı autocomplete sağlar.
---
## Yapılar
### FunctionInfo
| Alan | Tip | Açıklama |
|------|-----|----------|
| `name` | `&'static str` | Fonksiyon adı |
| `signature` | `&'static str` | İmza (örn: `"(min, max) -> Number"`) |
| `doc` | `Option<&'static str>` | Opsiyonel açıklama |
### MethodInfo
| Alan | Tip | Açıklama |
|------|-----|----------|
| `name` | `&'static str` | Metod adı |
| `signature` | `&'static str` | İmza (örn: `"() -> String"`) |
| `doc` | `Option<&'static str>` | Opsiyonel açıklama |
### VariableInfo
| Alan | Tip | Açıklama |
|------|-----|----------|
| `name` | `String` | Değişken adı |
| `type_name` | `String` | Tip adı: `String`, `Number`, `Boolean`, `NumberList`, `StringList`, `Object` |
| `doc` | `Option<String>` | Opsiyonel açıklama |
### LanguageInfo
Tüm metadata'yı toplayan ana yapı.
| Alan | Tip | Açıklama |
|------|-----|----------|
| `functions` | `Vec<FunctionInfo>` | Fonksiyon listesi |
| `methods` | `Vec<(&'static str, Vec<MethodInfo>)>` | Tipe göre metod listesi |
| `variables` | `Vec<VariableInfo>` | Değişken listesi |
---
## Metodlar
### `LanguageInfo::builtin() -> Self`
Tüm built-in fonksiyon ve metodları içeren yeni bir `LanguageInfo` oluşturur.
**Built-in fonksiyonlar:** `log`, `rand`
**Built-in metodlar (tipe göre):**
| Tip | Metodlar |
|-----|----------|
| `String` | `upper`, `lower`, `trim`, `trimStart`, `trimEnd`, `split`, `replace`, `contains`, `startsWith`, `endsWith`, `length`, `charAt`, `substring` |
| `Number` | *(yok)* |
| `Boolean` | *(yok)* |
| `NumberList` | `length`, `len`, `isEmpty`, `first`, `last`, `get`, `contains`, `indexOf`, `slice`, `reverse`, `sort`, `sum`, `avg`, `min`, `max` |
| `StringList` | `length`, `len`, `isEmpty`, `first`, `last`, `get`, `contains`, `indexOf`, `slice`, `reverse`, `sort`, `join` |
| `Object` | `keys`, `values`, `length`, `len`, `contains`, `get` |
### `add_function(name, signature, doc)`
Host-kayıtlı fonksiyon ekler (VM'deki `register_function` ile eşleşir).
### `add_method(type_name, name, signature, doc)`
Host-kayıtlı metod ekler (VM'deki `register_method` ile eşleşir). Belirtilen tipe ait metod listesine eklenir.
### `add_variable(name, type_name, doc)`
External değişken ekler (VM'deki `set_global` ile eşleşir). Editörde autocomplete ve tip-bazlı metod önerileri için kullanılır.
### `to_json() -> String`
Tüm metadata'yı JSON formatında serileştirir. Frontend editör kütüphanesine gönderilecek çıktıyı üretir.
---
## JSON Formatı
`to_json()` çıktısı:
```json
{
"functions": [
{"name": "log", "signature": "(...args) -> null", "doc": "Print values to output"},
{"name": "getRate", "signature": "(code: String) -> Number", "doc": "Get exchange rate"}
],
"methods": {
"String": [
{"name": "upper", "signature": "() -> String"},
{"name": "toTitleCase", "signature": "() -> String", "doc": "Custom host method"}
],
"Number": [],
"NumberList": [
{"name": "sum", "signature": "() -> Number"}
],
"StringList": [
{"name": "join", "signature": "(delim?: String) -> String"}
]
},
"variables": [
{"name": "price", "type": "Number"},
{"name": "category", "type": "String", "doc": "Product category"}
]
}
```
---
## Kullanım Örneği
```rust
use dexpr::language_info::LanguageInfo;
// 1. Built-in metadata oluştur
let mut info = LanguageInfo::builtin();
// 2. Host fonksiyonları ekle (register_function ile eşleşmeli)
info.add_function("getRate", "(code: String) -> Number", Some("Get exchange rate"));
info.add_function("fetchPrice", "(sku: String) -> Number", None);
// 3. Host metodları ekle (register_method ile eşleşmeli)
info.add_method("String", "toTitleCase", "() -> String", None);
// 4. External değişkenleri ekle (set_global ile eşleşmeli)
info.add_variable("price", "Number", None);
info.add_variable("category", "String", Some("Ürün kategorisi".to_string()));
info.add_variable("items", "StringList", None);
// 5. JSON üret ve frontend'e gönder
let json = info.to_json();
// json -> HTTP response, WebSocket message, vb.
```
---
## Frontend Entegrasyonu
Üretilen JSON doğrudan `codemirror-lang-dexpr` kütüphanesine verilir:
```typescript
import { dexpr } from "codemirror-lang-dexpr";
// Rust'tan gelen JSON parse edilir
const languageInfo = JSON.parse(jsonFromRust);
const extensions = [basicSetup, dexpr(languageInfo)];
```
Detaylar için: [Editor Modülü](editor.md)

95
docs/opcodes.md Normal file
View File

@@ -0,0 +1,95 @@
# Opcodes Modülü
**Konum:** `src/opcodes.rs`
Bytecode komut setini (instruction set) tanımlar. Her opcode bir `u8` değerine karşılık gelir.
---
## OpCodeByte Enum
### Register Operasyonları
| Opcode | Değer | Açıklama |
|--------|-------|----------|
| `LoadConst` | `0x10` | Sabit değeri register'a yükle |
| `Move` | `0x11` | Register'dan register'a kopyala |
### Bellek Operasyonları
| Opcode | Değer | Açıklama |
|--------|-------|----------|
| `LoadLocal` | `0x20` | Yerel değişkeni register'a yükle (ayrılmış) |
| `StoreLocal` | `0x21` | Register'ı yerel değişkene kaydet (ayrılmış) |
| `LoadGlobal` | `0x22` | Global değişkeni register'a yükle |
| `StoreGlobal` | `0x23` | Register'ı global değişkene kaydet |
> **Not:** `LoadLocal`/`StoreLocal` opcode'ları tanımlıdır ancak şu an compiler tarafından emit edilmez. Tüm değişkenler global scope'tadır.
### Aritmetik
| Opcode | Değer | Açıklama |
|--------|-------|----------|
| `Add` | `0x30` | Toplama |
| `Sub` | `0x31` | Çıkarma |
| `Mul` | `0x32` | Çarpma |
| `Div` | `0x33` | Bölme |
| `Neg` | `0x34` | Negatif (tekli) |
| `Mod` | `0x35` | Mod alma |
| `Pow` | `0x36` | Üs alma |
### Karşılaştırma
| Opcode | Değer | Açıklama |
|--------|-------|----------|
| `Lt` | `0x40` | Küçüktür |
| `Lte` | `0x41` | Küçük eşit |
| `Gt` | `0x42` | Büyüktür |
| `Gte` | `0x43` | Büyük eşit |
| `Eq` | `0x44` | Eşit |
| `Neq` | `0x45` | Eşit değil |
### Boolean Mantık
| Opcode | Değer | Açıklama |
|--------|-------|----------|
| `And` | `0x50` | Mantıksal VE |
| `Or` | `0x51` | Mantıksal VEYA |
| `Not` | `0x52` | Mantıksal DEĞİL |
### Kontrol Akışı
| Opcode | Değer | Açıklama |
|--------|-------|----------|
| `Jump` | `0x60` | Koşulsuz atlama |
| `JumpIfFalse` | `0x61` | Register false ise atla |
### Üyelik Testi
| Opcode | Değer | Açıklama |
|--------|-------|----------|
| `Contains` | `0x53` | Değer listede/string'de var mı kontrolü (`in` operatörü) |
### Özel Operasyonlar
| Opcode | Değer | Açıklama |
|--------|-------|----------|
| `Concat` | `0x80` | String birleştirme |
| `MethodCall` | `0x90` | Metod çağrısı |
| `GetProperty` | `0x91` | Nesne özelliği oku (dest, obj_reg, property_name_string) |
| `SetProperty` | `0x92` | Nesne özelliği yaz (obj_reg, property_name_string, val_reg) |
| `Log` | `0xA0` | Değer yazdır (built-in) |
| `CallExternal` | `0xA1` | Harici (host) fonksiyon çağrısı |
| `SetResult` | `0xB0` | İfade sonucunu kaydet (return value için) |
| `End` | `0xFF` | Program sonu |
---
## Hızlı Lookup Tablosu
`LOOKUP[256]` statik dizisi, O(1) karmaşıklıkta byte-to-opcode dönüşümü sağlar. `from_byte(u8)` metodu bu tabloyu kullanır.
**Metodlar:**
- `to_byte() -> u8` — Opcode'u byte'a dönüştür
- `from_byte(u8) -> Option<OpCodeByte>` — Byte'ı opcode'a dönüştür (lookup tablosu ile)
- `name() -> &str` — İnsan okunabilir opcode adı

115
docs/parser.md Normal file
View File

@@ -0,0 +1,115 @@
# Parser Modülü
**Konum:** `src/parser/`
Parser, kaynak kodu AST'ye dönüştürür. PEG (Parsing Expression Grammar) tabanlıdır ve `peg` crate'ini kullanır.
---
## Giriş Noktaları
**Dosya:** `src/parser/mod.rs`
| Fonksiyon | Dönüş | Açıklama |
|-----------|-------|----------|
| `program(source)` | `Vec<Stmt>` | Tam programı parse eder |
| `program_with_spans(source)` | `Vec<(usize, Stmt)>` | Byte offset'leriyle birlikte parse eder |
### `offset_to_span(source, offset) -> Span`
Byte offset'ini 1-indexed satır ve sütuna dönüştürür. UTF-8 karakter sınırlarını doğru şekilde hesaplar.
---
## Gramer Kuralları
**Dosya:** `src/parser/grammar.rs`
### Deyim (Statement) Ayrıştırma
| Kural | Ürettiği | Sözdizimi |
|-------|----------|-----------|
| `statement()` | `Stmt` | Herhangi bir geçerli deyim |
| `assignment()` | `Stmt::Assignment` | `x = expr` veya `x += expr` |
| `property_assignment()` | `Stmt::PropertyAssignment` | `a.b.c = expr` |
| `expr_stmt()` | `Stmt::ExprStmt` | Bağımsız ifade |
| `if_stmt()` | `Stmt::If` | `if cond then ... [else ...] end` |
**Bileşik atamalar:** `+=`, `-=`, `*=`, `/=`, `%=` operatörleri desugar edilir:
- `x += e``x = x + e`
**Özellik ataması:** `identifier ("." identifier)+ "=" expression` kalıbı ile nesne özelliklerine atama yapılır (örn: `a.b.c = 42`).
**Else zincirleri:** `else if` yapısı desteklenir ve rekürsif olarak parse edilir.
### İfade (Expression) Ayrıştırma - Öncelik Sırası
En düşükten en yükseğe:
1. Method çağrıları, fonksiyon çağrıları
2. Mantıksal AND (`&&`)
3. Mantıksal OR (`||`)
4. Karşılaştırma (`==`, `!=`, `<`, `<=`, `>`, `>=`, `in`)
5. Toplama/Çıkarma (`+`, `-`)
6. Çarpma/Bölme/Mod (`*`, `/`, `%`)
7. Üs alma (`**`) - sağdan birleşimli (right-associative)
8. Tekli operatörler (`-`, `!`), atomik ifadeler
### Postfix Kuralı
`postfix()` kuralı, herhangi bir atom üzerinde `.property` (özellik erişimi) ve `.method(args)` (metod çağrısı) zincirlemesini sağlar. Atom parse edildikten sonra ardışık `.identifier` veya `.identifier(args)` kalıpları uygulanır:
- `obj.field``Expr::PropertyAccess(obj, "field")`
- `obj.method(args)``Expr::MethodCall(obj, "method", args)`
- `obj.a.b.method()` → zincirleme erişim ve çağrı
### Atomik İfadeler
`atom()` kuralı temel ifade birimlerini parse eder:
| Tür | Örnek | Ürettiği |
|-----|-------|----------|
| Tanımlayıcı | `myVar` | `Expr::Variable` |
| Sayı | `42`, `3.14` | `Expr::Value(Number)` |
| String | `"hello"`, `'world'` | `Expr::Value(String)` |
| Boolean | `true`, `false` | `Expr::Value(Boolean)` |
| Parantezli ifade | `(a + b)` | İç ifade |
| Tekli negatif | `-x` | `Expr::UnaryOp(Neg, x)` |
| Tekli NOT | `!flag` | `Expr::UnaryOp(Not, flag)` |
### Literal Ayrıştırma
- **Sayılar:** Ondalık kısım opsiyonel (`42`, `3.14`)
- **Stringler:** Çift veya tek tırnak, escape sequence destekli
- **Tanımlayıcılar:** Ayrılmış kelimelerle çakışmamalı
### Ayrılmış Kelimeler
`if`, `then`, `else`, `end`, `true`, `false`, `in`
### Boşluk ve Yorumlar
- Boşluklar: space, tab, newline
- Satır yorumu: `// ...`
- Blok yorumu: `/* ... */`
---
## Hata Yönetimi
Parser hataları `peg` crate'inden gelir ve beklenen token/kural bilgisi içerir. `program_with_spans()` kullanıldığında byte offset'leri `offset_to_span()` ile satır/sütun bilgisine dönüştürülebilir.
---
## Kullanım Örneği
```rust
use dexpr::parser;
let source = r#"
result = (3 + 4) * 2
log(result)
"#;
let ast = parser::program(source).expect("Parse error");
// ast: Vec<Stmt> - Assignment, ExprStmt
```

202
docs/vm.md Normal file
View File

@@ -0,0 +1,202 @@
# VM (Virtual Machine) Modülü
**Konum:** `src/vm/`
Register tabanlı sanal makine. Bytecode'u çalıştırır, 8 register ve global değişken deposu içerir.
---
## Alt Modüller
| Dosya | İçerik |
|-------|--------|
| `vm/mod.rs` | Modül export'ları |
| `vm/vm.rs` | Ana VM implementasyonu |
| `vm/error.rs` | Hata türleri (VMError) |
| `vm/debug_info.rs` | Bytecode offset → kaynak konum eşleştirme |
---
## VMError (Hata Türleri)
**Dosya:** `src/vm/error.rs`
| Hata | Açıklama |
|------|----------|
| `TypeMismatch { expected, got }` | Tip uyuşmazlığı |
| `UndefinedVariable(SmolStr)` | Tanımlanmamış değişken |
| `DivisionByZero` | Sıfıra bölme |
| `BytecodeError(String)` | Bozuk bytecode |
| `MethodNotFound { type_name, method }` | Metod bulunamadı |
| `RuntimeError(String)` | Genel çalışma zamanı hatası |
| `InvalidOperation { operation, left_type, right_type }` | Desteklenmeyen operasyon |
| `WithLocation { span, message }` | Kaynak konum bilgili hata |
`with_span(span)` metodu hatayı kaynak konum bilgisi ile sarar (çift sarmalamayı önler).
---
## DebugInfo
**Dosya:** `src/vm/debug_info.rs`
Bytecode offset'lerini kaynak kod konumlarına (Span) eşleştirir. Run-length encoded yapıda: her girdi bir sonraki girdiye kadar geçerlidir.
```rust
struct DebugInfo {
entries: Vec<(u32, Span)>, // sıralı (offset, span) çiftleri
}
```
**Metodlar:**
- `add_entry(offset, span)` — Eşleştirme ekle (artan offset sırasında)
- `get_span(offset) -> Option<Span>` — Binary search ile konum bul
---
## VM Yapısı
```rust
struct VM<'a> {
// Bytecode durumu
bytecode: &'a [u8],
reader: BytecodeReader<'a>,
pc: usize,
// Hesaplama
registers: [Value; 8], // 8 register
// Değişkenler
globals: Map<SmolStr, Value, 64>, // Global kapsam (max 64 giriş)
// Kaynaklar
heap: Bump, // Bump allocator
// Debug
debug_info: Option<DebugInfo>,
debug: bool, // Debug çıktısı (debug build'lerde)
opcode_counts: [usize; 256], // Profiling (debug build'lerde)
}
```
---
## Başlatma ve API
| Metod | Açıklama |
|-------|----------|
| `new(bytecode)` | VM oluştur |
| `set_debug_info(debug_info)` | Kaynak konum eşleştirmesi sağla |
| `set_global(name, value)` | Global değişken ata |
| `get_global(name) -> Option<&Value>` | Global değişken oku |
| `register_function(name, fn)` | Harici (host) fonksiyon kaydet |
| `register_method(type_name, method_name, fn)` | Tipe özel harici metod kaydet |
| `reset()` | Durumu sıfırla, yeniden çalıştırmaya hazırla |
---
## Çalıştırma Döngüsü
`execute() -> Result<Value, VMError>` (son expression'ın değerini döndürür):
1. VM durumunu sıfırla
2. Bytecode kaldığı sürece döngüde çalış:
- Opcode byte'ını oku
- İlgili handler'a yönlendir
- Hataları kaynak konum bilgisi ile sar
- Profiling verilerini güncelle (debug build)
- `End` opcode'unda dur
---
## Opcode Handler'ları
### Register ve Bellek
- **`handle_load_const()`** — Bytecode'dan değer oku, register'a koy
- **`handle_move()`** — Register → register kopyala
- **`handle_load_global()`** — Global map → register
- **`handle_store_global()`** — Register → global map
### Aritmetik
- **`binary_op(f, name)`** — Genel handler: iki operand register'ı oku, fonksiyonu uygula, sonucu kaydet
- Sıfıra bölme kontrolü yapılır
- **`handle_neg()`** — Sadece Number tipinde tekli negatif
### Karşılaştırma
- **`compare_op(f, name)`** — Decimal değerler üzerinde karşılaştırma, Boolean döndürür
### Boolean
- **`handle_and()`**, **`handle_or()`**, **`handle_not()`** — Boolean register'lar üzerinde mantık operasyonları
### Kontrol Akışı
- **`handle_jump()`** — 4-byte adres oku, reader pozisyonunu ayarla
- **`handle_jump_if_false()`** — Register `Boolean(false)` ise atla
### String, Nesne ve Metodlar
- **`handle_concat()`** — İki String register'ını birleştir
- **`handle_get_property()`** — Object register'ından alan oku, alan yoksa `Null` döndür
- **`handle_set_property()`** — Object register'ında alan değerini ayarla
- **`handle_method_call()`** — Nesne register'ı, metod adı, argümanlar
- **String metodları:** `upper`, `lower`, `trim`, `trimStart`, `trimEnd`, `split(delimiter)`, `replace(old, new)`, `startsWith(prefix)`, `endsWith(suffix)`, `contains(substr)`, `length`, `charAt(index)`, `substring(start, end?)`
- **StringList metodları:** `length`/`len`, `isEmpty`, `first`, `last`, `get(index)`, `contains(value)`, `indexOf(value)`, `slice(start, end?)`, `reverse()`, `sort()`, `join(delimiter?)`
- **NumberList metodları:** `length`/`len`, `isEmpty`, `first`, `last`, `get(index)`, `contains(value)`, `indexOf(value)`, `slice(start, end?)`, `reverse()`, `sort()`, `sum`, `avg`, `min`, `max`
- **Object metodları:** `keys()`, `values()`, `length`/`len()`, `contains(key)`, `get(key)`
- **Harici metodlar:** Yukarıdaki built-in metodlar bulunamazsa `external_methods` HashMap'inde aranır
### Üyelik Testi
- **`handle_contains()`** — `in` operatörü: String in StringList, Number in NumberList, String in String (substring), String in Object (anahtar varlığı kontrolü)
### Harici Fonksiyonlar ve Sonuç
- **`handle_call_external()`** — İsimle harici fonksiyon çağır (HashMap lookup)
- **`handle_set_result()`** — ExprStmt sonucunu `last_result`'a kaydet
### Built-in
- **`handle_log()`** — Register değerini stdout'a yazdır
- **`rand(min, max)`** — min ile max arasında rastgele tamsayı üret (varsayılan harici fonksiyon)
---
## Yardımcı Metodlar
| Metod | Açıklama |
|-------|----------|
| `read_register_checked()` | Register oku ve doğrula |
| `validate_register()` | Register indeks sınır kontrolü |
| `read_jump_address()` | 4-byte adres oku ve sınır kontrolü yap |
| `set_position(addr)` | Reader pozisyonunu doğrulayarak ayarla |
| `wrap_error(err)` | Hataya kaynak konum bilgisi ekle |
| `debug_print_state()` | Debug çıktısı (debug build) |
| `print_profile_summary()` | Opcode çalışma sayıları (debug build) |
---
## Harici Fonksiyon ve Metod Kaydı
```rust
// Harici fonksiyon
vm.register_function("getRate", |args| {
match &args[0] {
Value::String(currency) => Ok(Value::Number(dec!(34.5))),
_ => Err("expected string".to_string()),
}
});
// Tipe özel harici metod
vm.register_method("Number", "format", |this, args| {
// this: &Value, args: &[Value]
Ok(Value::String("formatted".into()))
});
```
**Tip isimleri:** `"Number"`, `"String"`, `"Boolean"`, `"NumberList"`, `"StringList"`, `"Object"`, `"Null"`
---
## Çalışma Modeli Özeti
- **Register tabanlı:** 8 register ile hesaplama
- **Global depo:** SmolStr → Value map'i
- **Harici fonksiyonlar:** İsimle çözümlenen host fonksiyonları (HashMap lookup)
- **Harici metodlar:** Tipe özel host metodları
- **Expression return:** Son ExprStmt'ın değeri `execute()` dönüş değeri olarak verilir
- **Dinamik tip sistemi:** Tip uyuşmazlıklarında çalışma zamanı hatası