mirror of
https://github.com/duhanbalci/dexpr.git
synced 2026-07-01 16:19:16 +00:00
269 lines
9.2 KiB
Markdown
269 lines
9.2 KiB
Markdown
# 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 field'ları + Object metodları |
|
||
| `kalemler.` (config'de `List`) | Element field'ları (property projection) + List metodları |
|
||
| `kalemler.tutar.` (List projection → `NumberList`) | NumberList metodları (sum, avg, min, max...) |
|
||
| `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" \| "List"` |
|
||
| `FunctionInfo` | Fonksiyon metadata'sı |
|
||
| `MethodInfo` | Metod metadata'sı |
|
||
| `VariableInfo` | Değişken metadata'sı |
|