mirror of
https://github.com/duhanbalci/dexpr.git
synced 2026-07-01 16:19:16 +00:00
initial commit
This commit is contained in:
91
docs/architecture.md
Normal file
91
docs/architecture.md
Normal 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
115
docs/ast.md
Normal 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
79
docs/bytecode.md
Normal 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
150
docs/compiler.md
Normal 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
266
docs/editor.md
Normal 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
294
docs/embedding.md
Normal 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
156
docs/language_info.md
Normal 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
95
docs/opcodes.md
Normal 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
115
docs/parser.md
Normal 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
202
docs/vm.md
Normal 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ı
|
||||
Reference in New Issue
Block a user