1. Giriş

Expr, Go diline özgü basit sözdizimi ve güçlü performans özellikleri ile bilinen bir dinamik yapılandırma çözümüdür. Expr ifade motorunun çekirdeği, güvenlik, hız ve sezgisellik üzerine odaklanmış olup, erişim kontrolü, veri filtreleme ve kaynak yönetimi gibi senaryolar için uygun hale getirilmiştir. Go'ya uygulandığında, Expr uygulamaların dinamik kuralları işleme yeteneğini büyük ölçüde artırır. Diğer dillerdeki yorumlayıcılar veya komut dosyası motorlarının aksine, Expr statik tür denetimi benimser ve yürütme için bayt kodu üretir, böylelikle performansı ve güvenliği sağlar.

2. Expr'in Yüklenmesi

Expr ifade motorunu Go dilinin paket yönetim aracı go get kullanılarak yükleyebilirsiniz:

go get github.com/expr-lang/expr

Bu komut, Expr kütüphane dosyalarını indirir ve bunları Go projenize yükleyerek, Go kodunuzda Expr'i içe aktarmanıza ve kullanmanıza olanak tanır.

3. Hızlı Başlangıç

3.1 Temel İfadelerin Derlenmesi ve Çalıştırılması

Basit bir örnek ile başlayalım: Basit bir ifade yazma, derleme ve ardından sonucu elde etmek için çalıştırma.

package main

import (
	"fmt"
	"github.com/expr-lang/expr"
)

func main() {
	// Temel bir toplama ifadesini derleme
	program, err := expr.Compile(`2 + 2`)
	if err != nil {
		panic(err)
	}

	// Ortam iletişimini geçmeden derlenmiş ifadeyi çalıştırma, çünkü burada değişkenlere ihtiyaç yok
	output, err := expr.Run(program, nil)
	if err != nil {
		panic(err)
	}

	// Sonucu yazdırma
	fmt.Println(output)  // 4 çıktısını verir
}

Bu örnekte, 2 + 2 ifadesi yürütülebilir bayt koda derlenir, ardından çıktı üretmek için yürütülür.

3.2 Değişken İfadelerin Kullanımı

Daha sonra, değişken içeren bir ortam oluşturacağız, bu değişkenleri kullanan bir ifade yazacağız, bu ifadeyi derleyip çalıştıracağız.

package main

import (
	"fmt"
	"github.com/expr-lang/expr"
)

func main() {
	// Değişkenler içeren bir ortam oluşturma
	env := map[string]interface{}{
		"foo": 100,
		"bar": 200,
	}

	// Ortamı kullanan değişken içeren bir ifadeyi derleme
	program, err := expr.Compile(`foo + bar`, expr.Env(env))
	if err != nil {
		panic(err)
	}

	// İfadeyi çalıştırma
	output, err := expr.Run(program, env)
	if err != nil {
		panic(err)
	}

	// Sonucu yazdırma
	fmt.Println(output)  // 300 çıktısını verir
}

Bu örnekte, env ortamı foo ve bar değişkenlerini içerir. foo + bar ifadesi derleme sırasında değişkenlerin tiplerini çevrimdışı olarak alır ve çalışma zamanında bu değişkenlerin değerlerini kullanarak ifadenin sonucunu değerlendirir.

4. Detaylı Olarak Expr Sözdizimi

4.1 Değişkenler ve Söz Dizimi

Expr ifade motoru, sayılar, dizeler ve boolean değerler gibi yaygın veri türü literallerini işleyebilir. Literaller, kod içinde doğrudan yazılan veri değerlerini ifade eder, örneğin 42, "hello" ve true.

Sayılar

Expr'de tamsayıları ve ondalık sayıları doğrudan yazabilirsiniz:

42      // Tamsayı 42'yi temsil eder
3.14    // Ondalık sayı 3.14'ü temsil eder

Dizeler

Dize literalleri çift tırnak " veya ters tırnak `` ile çevrilidir. Örneğin:

"hello, world" // Çift tırnak içinde çevrili dize, kaçış karakterlerini destekler
`hello, world` // Ters tırnak içinde çevrili dize, kaçış karakterlerini desteklemeden dize biçimini korur

Boolean Değerler

Yalnızca iki boolean değer vardır, true ve false, mantıksal doğru ve yanlışı temsil eder:

true   // Boolean doğru değeri
false  // Boolean yanlış değeri

Değişkenler

Expr ayrıca ortamdaki değişkenlerin tanımlanmasına izin verir ve daha sonra ifadede bu değişkenlere başvurabilirsiniz. Örneğin:

env := map[string]interface{}{
    "age": 25,
    "name": "Alice",
}

Ardından ifadede age ve namee başvurabilirsiniz:

age > 18  // Yaş 18'den büyük mü diye kontrol etme
name == "Alice"  // İsim "Alice"e eşit mi diye belirleme

4.2 Operatörler

Expr ifade motoru, aritmetik operatörler, mantıksal operatörler, karşılaştırma operatörleri, ve küme operatörleri gibi çeşitli operatörleri destekler.

Aritmetik ve Mantıksal Operatörler

Aritmetik operatörler toplama (+), çıkarma (-), çarpma (*), bölme (/) ve mod (%) içerir. Mantıksal operatörler ise mantıksal VE (&&), mantıksal VEYA (||) ve mantıksal DEĞİL (!) gibi operatörleri içerir, örneğin:

2 + 2 // Sonuç 4'tür
7 % 3 // Sonuç 1'dir
!true // Sonuç false'tır
age >= 18 && name == "Alice" // Yaş 18'den küçük değilse ve isim "Alice" ise kontrol edin

Karşılaştırma Operatörleri

Karşılaştırma operatörleri eşit (==), eşit değil (!=), küçük (<), küçük eşit (<=), büyük (>), ve büyük eşit (>=) gibi iki değeri karşılaştırmak için kullanılır:

age == 25 // Yaş 25'e eşit mi diye kontrol edin
age != 18 // Yaş 18'e eşit değil mi diye kontrol edin
age > 20  // Yaş 20'den büyük mü diye kontrol edin

Set Operatörleri

Expr ayrıca setlerle çalışmak için bazı operatörler sağlar, örneğin bir elemanın set içinde olup olmadığını kontrol etmek için in kullanılır. Setler array'ler, slice'lar ya da map'ler olabilir:

"user" in ["user", "admin"]  // "user" array içinde olduğu için true, 
3 in {1: true, 2: false}     // 3 map içinde bir key değil, bu yüzden false

Ayrıca all, any, one ve none gibi bazı gelişmiş set işlem fonksiyonları bulunmaktadır, bu fonksiyonlar anonim fonksiyonları (lambda) kullanmayı gerektirir:

all(tweets, {.Len <= 240})  // Tüm tweet'lerin Len alanı 240 karakteri geçmiyor mu diye kontrol edin
any(tweets, {.Len > 200})   // Tweet'ler arasında Len alanı 200 karakteri geçen var mı diye kontrol edin

Üye Operatörü

Expr ifade dilinde üye operatörü, Go dilindeki struct'ın özelliklerine erişmemize olanak tanır. Bu özellik, Expr'in doğrudan karmaşık veri yapılarını manipüle etmesine olanak tanır ve çok esnek ve pratik hale getirir.

Üye operatörünü kullanmak çok basittir, sadece . operatörünü kullanın ve ardından özellik ismini yazın. Örneğin, eğer aşağıdaki struct'e sahipsek:

type User struct {
    Name string
    Age  int
}

User yapısının Name özelliğine erişmek için şöyle bir ifade yazabilirsiniz:

env := map[string]interface{}{
    "user": User{Name: "Alice", Age: 25},
}

code := `user.Name`

program, err := expr.Compile(code, expr.Env(env))
if err != nil {
    panic(err)
}

output, err := expr.Run(program, env)
if err != nil {
    panic(err)
}

fmt.Println(output) // Çıktı: Alice

nil Değerlerinin İşlenmesi

Özelliklere erişirken nesnenin nil olabileceği durumlarla karşılaşabilirsiniz. Expr, güvenli bir özellik erişimi sağlar, böylece struct veya iç içe özellik nil olsa bile çalışma zamanı hata atfetmez.

Özelliklere başvurmak için ?. operatörünü kullanın. Nesne nil ise hata fırlatmak yerine nil döndürecektir.

author.User?.Name

Eşdeğer ifade

author.User != nil ? author.User.Name : nil

?? operatörü genellikle varsayılan değerleri döndürmek içindir:

author.User?.Name ?? "Anonymous"

Eşdeğer ifade

author.User != nil ? author.User.Name : "Anonymous"

Pipe Operator

Expr'deki pipe operatörü (|), bir ifadenin sonucunu başka bir ifadenin parametresi olarak iletmek için kullanılır. Bu, Unix kabuğundaki pipe işlemine benzer ve birden çok işlevsel modülün bir işleme hattı oluşturmak üzere birbirine bağlanmasına olanak tanır. Expr'de kullanarak daha net ve özlü ifadeler oluşturmak mümkündür.

Örneğin, bir kullanıcının adını almak için bir işlev ve bir karşılama mesajı için bir şablonumuz olsun:

env := map[string]interface{}{
    "user":      User{Name: "Bob", Age: 30},
    "get_name":  func(u User) string { return u.Name },
    "greet_msg": "Merhaba, %s!",
}

code := `get_name(user) | sprintf(greet_msg)`

program, err := expr.Compile(code, expr.Env(env))
if err != nil {
    panic(err)
}

output, err := expr.Run(program, env)
if err != nil {
    panic(err)
}

fmt.Println(output) // Çıktı: Merhaba, Bob!

Bu örnekte, önce get_name(user) ile kullanıcının adını alıyoruz, ardından ismi sprintf işlevine | pipe operatörü kullanarak ileterek son karşılama mesajını oluşturuyoruz.

Pipe operatörü kullanarak kodumuzu modüler hale getirebilir, kod yeniden kullanılabilirliğini artırabilir ve ifadeleri daha okunabilir hale getirebiliriz.

4.3 İşlevler

Expr, yerleşik işlevleri ve özel işlevleri destekleyerek ifadeleri daha güçlü ve esnek hale getirir.

Yerleşik İşlevlerin Kullanımı

len, all, none, any gibi yerleşik işlevler doğrudan ifade içinde kullanılabilir.

// Yerleşik bir işlevin kullanım örneği
program, err := expr.Compile(`all(users, {.Age >= 18})`, expr.Env(env))
if err != nil {
    panic(err)
}

// Not: burada env değişkeninin users'ı içermesi gerekir ve her kullanıcının Age özelliğine sahip olması gerekir
output, err := expr.Run(program, env)
fmt.Print(output) // Eğer env içindeki tüm kullanıcıların yaşı 18 veya üzeriyse, true dönecektir

Özel İşlevlerin Tanımlanması ve Kullanılması

Expr'de, çevre eşlemesine işlev tanımlamalarını aktararak özel işlevler oluşturabilirsiniz.

// Özel işlev örneği
env := map[string]interface{}{
    "greet": func(name string) string {
        return fmt.Sprintf("Merhaba, %s!", name)
    },
}

program, err := expr.Compile(`greet("Dünya")`, expr.Env(env))
if err != nil {
    panic(err)
}

output, err := expr.Run(program, env)
fmt.Print(output) // Çıktı: Merhaba, Dünya!

Expr'deki işlevleri kullanırken, kodunuzu modüler hale getirebilir ve karmaşık mantığı ifadelere entegre edebilirsiniz. Değişkenler, operatörler ve işlevlerin birleştirilmesiyle, Expr güçlü ve kullanımı kolay bir araç haline gelir. Expr çevresini oluştururken ve ifadeleri çalıştırırken her zaman veri türü güvenliğini sağlamayı unutmayın.

5. Yerleşik İşlev Belgelendirmesi

Expr ifade motoru, geliştiricilere çeşitli karmaşık senaryolarla başa çıkmak için zengin bir yerleşik işlev kümesi sunar. Aşağıda, bu yerleşik işlevleri ve kullanımlarını detaylandıracağız.

all

all işlevi, bir koleksiyondaki tüm öğelerin belirli bir koşulu karşılayıp karşılamadığını kontrol etmek için kullanılır. İki parametre alır: koleksiyon ve koşul ifadesi.

// Tüm tweetlerin içeriğinin 240 karakterden az olduğunu kontrol etme
code := `all(tweets, len(.Content) < 240)`

any

all ile benzer şekilde, any işlevi bir koleksiyondaki herhangi bir öğenin bir koşulu karşılayıp karşılamadığını kontrol etmek için kullanılır.

// Herhangi bir tweetin içeriğinin 240 karakterden fazla olduğunu kontrol etme
code := `any(tweets, len(.Content) > 240)`

none

none işlevi, bir koleksiyondaki hiçbir öğenin belirli bir koşulu karşılamadığını kontrol etmek için kullanılır.

// Tekrarlanan hiçbir tweetin olmadığını kontrol etme
code := `none(tweets, .IsRepeated)`

one

one işlevi, bir koleksiyondaki yalnızca bir öğenin belirli bir koşulu karşıladığını doğrulamak için kullanılır.

// Yalnızca bir tweetin belirli bir anahtar kelimeyi içerdiğini kontrol etme
code := `one(tweets, contains(.Content, "anahtar kelime"))`

filter

filter işlevi, belirli bir koşulu karşılayan koleksiyon öğelerini filtrelemek için kullanılır.

// Öncelikli olarak işaretlenmiş tüm tweetleri filtreleme
code := `filter(tweets, .IsPriority)`

map

map işlevi, bir koleksiyondaki öğeleri belirtilen bir yönteme göre dönüştürmek için kullanılır.

// Tüm tweetlerin yayın zamanını biçimlendirme
code := `map(tweets, {.PublishTime: Format(.Date)})`

len

len fonksiyonu, bir koleksiyonun veya dizenin uzunluğunu döndürmek için kullanılır.

// Kullanıcı adının uzunluğunu alma
code := `len(username)`

contains

contains fonksiyonu, bir dizenin belirli bir alt dizeyi içerip içermediğini veya bir koleksiyonun belirli bir öğeyi içerip içermediğini kontrol etmek için kullanılır.

// Kullanıcı adının yasaklı karakterleri içerip içermediğini kontrol etme
code := `contains(username, "yasaklı karakterler")`

Yukarıda bahsedilenler, Expr ifade motoru tarafından sağlanan yerleşik fonksiyonların sadece bir parçasıdır. Bu güçlü fonksiyonlarla, verileri ve mantığı daha esnek ve verimli bir şekilde işleyebilirsiniz. Daha detaylı fonksiyon listesi ve kullanım talimatları için lütfen resmi Expr belgelerine başvurun.