1. Pendahuluan
Expr adalah sebuah solusi konfigurasi dinamis yang dirancang untuk bahasa Go, dikenal karena sintaksis sederhananya dan fitur kinerja yang kuat. Inti dari mesin ekspresi Expr difokuskan pada keamanan, kecepatan, dan intuitivitas, menjadikannya cocok untuk skenario seperti pengendalian akses, penyaringan data, dan pengelolaan sumber daya. Ketika diterapkan pada Go, Expr sangat meningkatkan kemampuan aplikasi untuk menangani aturan-aturan dinamis. Berbeda dengan penerjemah atau mesin skrip di bahasa lain, Expr mengadopsi pemeriksaan tipe statis dan menghasilkan bytecode untuk eksekusi, memastikan kinerja dan keamanan.
2. Instalasi Expr
Anda dapat menginstal mesin ekspresi Expr menggunakan alat manajemen paket bahasa Go, yaitu go get
:
go get github.com/expr-lang/expr
Perintah ini akan mengunduh file-file pustaka Expr dan menginstalnya ke dalam proyek Go Anda, memungkinkan Anda untuk mengimpor dan menggunakan Expr dalam kode Go Anda.
3. Memulai dengan Cepat
3.1 Compile dan Jalankan Ekspresi Dasar
Mari mulai dengan contoh dasar: menulis ekspresi sederhana, mengompilasinya, dan kemudian menjalankannya untuk mendapatkan hasilnya.
package main
import (
"fmt"
"github.com/expr-lang/expr"
)
func main() {
// Mengompilasi ekspresi penjumlahan dasar
program, err := expr.Compile(`2 + 2`)
if err != nil {
panic(err)
}
// Menjalankan ekspresi yang telah dikompilasi tanpa melewati lingkungan, karena tidak ada variabel yang diperlukan di sini
output, err := expr.Run(program, nil)
if err != nil {
panic(err)
}
// Mencetak hasil
fmt.Println(output) // Menghasilkan 4
}
Pada contoh ini, ekspresi 2 + 2
dikompilasi menjadi bytecode yang dapat dieksekusi, yang kemudian dijalankan untuk menghasilkan output.
3.2 Menggunakan Ekspresi Variabel
Selanjutnya, kita akan membuat sebuah lingkungan yang berisi variabel, menulis sebuah ekspresi yang menggunakan variabel-variabel tersebut, mengompilasi dan menjalankan ekspresi tersebut.
package main
import (
"fmt"
"github.com/expr-lang/expr"
)
func main() {
// Membuat lingkungan dengan variabel-variabel
env := map[string]interface{}{
"foo": 100,
"bar": 200,
}
// Mengompilasi ekspresi yang menggunakan variabel dari lingkungan
program, err := expr.Compile(`foo + bar`, expr.Env(env))
if err != nil {
panic(err)
}
// Menjalankan ekspresi
output, err := expr.Run(program, env)
if err != nil {
panic(err)
}
// Mencetak hasil
fmt.Println(output) // Menghasilkan 300
}
Pada contoh ini, lingkungan env
berisi variabel foo
dan bar
. Ekspresi foo + bar
menerka tipe dari foo
dan bar
dari lingkungan selama kompilasi, dan menggunakan nilai-nilai dari variabel ini saat runtime untuk mengevaluasi hasil ekspresi.
4. Detail Syntax Expr
4.1 Variabel dan Literal
Mesin ekspresi Expr dapat menangani literal tipe data umum, termasuk angka, string, dan nilai boolean. Literal adalah nilai data yang ditulis langsung dalam kode, seperti 42
, "hello"
, dan true
.
Angka
Di Expr, Anda dapat langsung menulis bilangan bulat dan bilangan desimal:
42 // Mewakili bilangan bulat 42
3.14 // Mewakili bilangan desimal 3.14
String
Literal string diapit oleh tanda kutip ganda "
atau backticks ``. Sebagai contoh:
"hello, dunia" // String diapit oleh tanda kutip ganda, mendukung karakter escape
`hello, dunia` // String diapit oleh backticks, mempertahankan format string tanpa mendukung karakter escape
Boolean
Hanya ada dua nilai boolean, true
dan false
, mewakili benar dan salah logis:
true // Nilai boolean true
false // Nilai boolean false
Variabel
Expr juga memungkinkan definisi variabel dalam lingkungan, dan kemudian merujuk variabel-variabel ini dalam ekspresi. Sebagai contoh:
env := map[string]interface{}{
"umur": 25,
"nama": "Alice",
}
Lalu dalam ekspresi, Anda dapat merujuk ke umur
dan nama
:
umur > 18 // Memeriksa apakah umur lebih dari 18
nama == "Alice" // Menentukan apakah nama sama dengan "Alice"
4.2 Operator
Mesin ekspresi Expr mendukung berbagai operator, termasuk operator aritmatika, operator logika, operator perbandingan, dan operator set, dll.
Operator Aritmatika dan Logika
Operator aritmatika meliputi penambahan (+
), pengurangan (-
), perkalian (*
), pembagian (/
), dan modulo (%
). Operator logika meliputi logika AND (&&
), logika OR (||
), dan logika NOT (!
), misalnya:
2 + 2 // Hasilnya adalah 4
7 % 3 // Hasilnya adalah 1
!true // Hasilnya adalah false
umur >= 18 && nama == "Alice" // Periksa apakah umur tidak kurang dari 18 dan apakah nama sama dengan "Alice"
Operator Pembanding
Operator pembanding meliputi sama dengan (==
), tidak sama dengan (!=
), kurang dari (<
), kurang dari atau sama dengan (<=
), lebih besar dari (>
), dan lebih besar dari atau sama dengan (>=
), digunakan untuk membandingkan dua nilai:
umur == 25 // Periksa apakah umur sama dengan 25
umur != 18 // Periksa apakah umur tidak sama dengan 18
umur > 20 // Periksa apakah umur lebih besar dari 20
Operator Set
Expr juga menyediakan beberapa operator untuk bekerja dengan set, seperti in
untuk memeriksa apakah sebuah elemen ada dalam set. Set dapat berupa array, slice, atau peta:
"user" in ["user", "admin"] // true, karena "user" ada di dalam array
3 in {1: true, 2: false} // false, karena 3 bukan kunci dalam peta
Terdapat juga beberapa fungsi operasi set lanjutan, seperti all
, any
, one
, dan none
, yang memerlukan penggunaan fungsi anonim (lambda):
all(tweets, {.Len <= 240}) // Periksa apakah bidang Len dari semua tweets tidak melebihi 240
any(tweets, {.Len > 200}) // Periksa apakah ada sebuah bidang Len dalam tweets yang melebihi 200
Operator Anggota
Dalam bahasa ekspresi Expr, operator anggota memungkinkan kita untuk mengakses properti dari struct
dalam bahasa Go. Fitur ini memungkinkan Expr untuk langsung memanipulasi struktur data kompleks, sehingga sangat fleksibel dan praktis.
Menggunakan operator anggota sangatlah sederhana, hanya menggunakan operator .
diikuti dengan nama properti. Misalnya, jika kita memiliki struct
berikut:
type User struct {
Nama string
Umur int
}
Anda dapat menulis sebuah ekspresi untuk mengakses properti Nama
dari struktur User
seperti ini:
env := map[string]interface{}{
"user": User{Nama: "Alice", Umur: 25},
}
code := `user.Nama`
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) // Output: Alice
Menangani Nilai-nilai NULL
Ketika mengakses properti, Anda mungkin menghadapi situasi di mana objek adalah nil
. Expr menyediakan akses properti yang aman, sehingga bahkan jika struktur atau properti bersarang adalah nil
, itu tidak akan menyebabkan kesalahan panic pada saat runtime.
Gunakan operator ?.
untuk merujuk kepada properti. Jika objek adalah nil, akan mengembalikan nil daripada melemparkan kesalahan.
pengarang.Pengguna?.Nama
Ekspresi yang setara
pengarang.Pengguna != nil ? pengarang.Pengguna.Nama : nil
Penggunaan operator ??
utamanya adalah untuk mengembalikan nilai default:
pengarang.Pengguna?.Nama ?? "Anonymous"
Ekspresi yang setara
pengarang.Pengguna != nil ? pengarang.Pengguna.Nama : "Anonymous"
Operator Pipa
Operator pipa (|
) dalam Expr digunakan untuk meneruskan hasil dari satu ekspresi sebagai parameter ke ekspresi lainnya. Hal ini mirip dengan operasi pipa dalam shell Unix, memungkinkan beberapa modul fungsional dihubungkan bersama untuk membentuk pipa pemrosesan. Dalam Expr, penggunaan operator ini dapat membuat ekspresi lebih jelas dan ringkas.
Sebagai contoh, jika kita memiliki fungsi untuk mendapatkan nama pengguna dan sebuah template untuk pesan selamat datang:
env := map[string]interface{}{
"user": User{Name: "Bob", Age: 30},
"get_name": func(u User) string { return u.Name },
"greet_msg": "Hello, %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) // Output: Hello, Bob!
Pada contoh ini, kita pertama-tama mendapatkan nama pengguna melalui get_name(user)
, kemudian meneruskan nama tersebut ke fungsi sprintf
menggunakan operator pipa |
untuk menghasilkan pesan selamat datang akhir.
Penggunaan operator pipa dapat memodulisasi kode kita, meningkatkan reuseabilitas kode, dan membuat ekspresi lebih mudah dibaca.
4.3 Fungsi
Expr mendukung fungsi bawaan dan fungsi kustom, membuat ekspresi lebih kuat dan fleksibel.
Menggunakan Fungsi Bawaan
Fungsi bawaan seperti len
, all
, none
, any
, dll. dapat digunakan langsung dalam ekspresi.
// Contoh penggunaan fungsi bawaan
program, err := expr.Compile(`all(users, {.Age >= 18})`, expr.Env(env))
if err != nil {
panic(err)
}
// Catatan: di sini env harus berisi variabel pengguna, dan setiap pengguna harus memiliki properti Age
output, err := expr.Run(program, env)
fmt.Print(output) // Jika semua pengguna di env berusia 18 tahun atau lebih, akan mengembalikan true
Cara Mendefinisikan dan Menggunakan Fungsi Kustom
Di Expr, Anda dapat membuat fungsi kustom dengan memasukkan definisi fungsi dalam pemetaan lingkungan.
// Contoh fungsi kustom
env := map[string]interface{}{
"greet": func(name string) string {
return fmt.Sprintf("Hello, %s!", name)
},
}
program, err := expr.Compile(`greet("World")`, expr.Env(env))
if err != nil {
panic(err)
}
output, err := expr.Run(program, env)
fmt.Print(output) // Mengembalikan Hello, World!
Ketika menggunakan fungsi dalam Expr, Anda dapat memodulisasi kode Anda dan menggabungkan logika kompleks ke dalam ekspresi. Dengan menggabungkan variabel, operator, dan fungsi, Expr menjadi alat yang kuat dan mudah digunakan. Selalu pastikan keamanan tipe saat membangun lingkungan Expr dan menjalankan ekspresi.
5. Dokumentasi Fungsi Bawaan
Mesin ekspresi Expr menyediakan pengembang dengan rangkaian fungsi bawaan yang kaya untuk menangani berbagai skenario kompleks. Di bawah ini, kami akan rincikan fungsi bawaan ini dan penggunaannya.
all
Fungsi all
dapat digunakan untuk memeriksa apakah semua elemen dalam sebuah koleksi memenuhi kondisi tertentu. Fungsi ini mengambil dua parameter: koleksi dan ekspresi kondisi.
// Memeriksa apakah semua twit memiliki panjang konten kurang dari 240
code := `all(tweets, len(.Content) < 240)`
any
Mirip dengan all
, fungsi any
digunakan untuk memeriksa apakah ada elemen dalam sebuah koleksi yang memenuhi kondisi.
// Memeriksa apakah ada twit dengan panjang konten lebih dari 240
code := `any(tweets, len(.Content) > 240)`
none
Fungsi none
digunakan untuk memeriksa apakah tidak ada elemen dalam sebuah koleksi yang memenuhi kondisi.
// Memastikan tidak ada twit yang diulang
code := `none(tweets, .IsRepeated)`
one
Fungsi one
digunakan untuk memastikan bahwa hanya satu elemen dalam sebuah koleksi memenuhi kondisi.
// Memeriksa apakah hanya satu twit mengandung kata kunci tertentu
code := `one(tweets, contains(.Content, "keyword"))`
filter
Fungsi filter
dapat menyaring elemen koleksi yang memenuhi kondisi tertentu.
// Menyaring semua twit yang ditandai sebagai prioritas
code := `filter(tweets, .IsPriority)`
map
Fungsi map
digunakan untuk mentransformasi elemen dalam sebuah koleksi sesuai dengan metode yang ditentukan.
// Memformat waktu terbit semua twit
code := `map(tweets, {.PublishTime: Format(.Date)})`
len
Fungsi len
digunakan untuk mengembalikan panjang dari sebuah rangkaian data atau string.
// Mendapatkan panjang dari username
code := `len(username)`
contains
Fungsi contains
digunakan untuk memeriksa apakah sebuah string mengandung sub-string tertentu atau apakah sebuah kumpulan data mengandung elemen tertentu.
// Memeriksa apakah username mengandung karakter-karakter ilegal
code := `contains(username, "karakter ilegal")`
Yang disebutkan di atas hanyalah sebagian dari fungsi bawaan yang disediakan oleh mesin ekspresi Expr. Dengan fungsi-fungsi yang kuat ini, Anda dapat menangani data dan logika dengan lebih fleksibel dan efisien. Untuk daftar fungsi yang lebih lengkap dan petunjuk penggunaan, silakan lihat dokumentasi resmi Expr.