1. Introduzione
Expr è una soluzione di configurazione dinamica progettata per il linguaggio Go, noto per la sua semplice sintassi e potenti funzionalità di performance. Il cuore del motore di espressioni Expr è concentrato sulla sicurezza, velocità e intuitività, rendendolo adatto a scenari come il controllo degli accessi, il filtraggio dei dati e la gestione delle risorse. Quando applicato a Go, Expr migliora notevolmente la capacità delle applicazioni di gestire regole dinamiche. A differenza degli interpreti o motori di script in altri linguaggi, Expr adotta il controllo dei tipi statici e genera bytecode per l'esecuzione, garantendo sia prestazioni che sicurezza.
2. Installazione di Expr
È possibile installare il motore di espressioni Expr utilizzando lo strumento di gestione dei pacchetti del linguaggio Go go get
:
go get github.com/expr-lang/expr
Questo comando scaricherà i file della libreria Expr e li installerà nel tuo progetto Go, consentendoti di importare ed utilizzare Expr nel tuo codice Go.
3. Inizio Veloce
3.1 Compilazione ed Esecuzione di Espressioni di Base
Iniziamo con un esempio di base: scrivere un'espressione semplice, compilarla e quindi eseguirla per ottenere il risultato.
package main
import (
"fmt"
"github.com/expr-lang/expr"
)
func main() {
// Compilazione di un'espressione di base di somma
program, err := expr.Compile(`2 + 2`)
if err != nil {
panic(err)
}
// Esecuzione dell'espressione compilata senza passare un ambiente, poiché qui non servono variabili
output, err := expr.Run(program, nil)
if err != nil {
panic(err)
}
// Stampare il risultato
fmt.Println(output) // Stampa 4
}
In questo esempio, l'espressione 2 + 2
viene compilata in bytecode eseguibile, che viene quindi eseguito per produrre l'output.
3.2 Uso di Espressioni Variabili
Successivamente, creeremo un ambiente contenente variabili, scriveremo un'espressione che utilizza queste variabili, compileremo ed eseguiremo questa espressione.
package main
import (
"fmt"
"github.com/expr-lang/expr"
)
func main() {
// Creazione di un ambiente con variabili
env := map[string]interface{}{
"foo": 100,
"bar": 200,
}
// Compilazione di un'espressione che utilizza le variabili dall'ambiente
program, err := expr.Compile(`foo + bar`, expr.Env(env))
if err != nil {
panic(err)
}
// Esecuzione dell'espressione
output, err := expr.Run(program, env)
if err != nil {
panic(err)
}
// Stampare il risultato
fmt.Println(output) // Stampa 300
}
In questo esempio, l'ambiente env
contiene le variabili foo
e bar
. L'espressione foo + bar
deduce i tipi di foo
e bar
dall'ambiente durante la compilazione, e utilizza i valori di queste variabili durante l'esecuzione per valutare il risultato dell'espressione.
4. Sintassi di Expr in Dettaglio
4.1 Variabili e Letterali
Il motore di espressioni Expr può gestire letterali di tipo di dati comuni, inclusi numeri, stringhe e valori booleani. I letterali sono valori dati scritti direttamente nel codice, come 42
, "ciao"
, e true
.
Numeri
In Expr, è possibile scrivere direttamente numeri interi e numeri in virgola mobile:
42 // Rappresenta il numero intero 42
3.14 // Rappresenta il numero in virgola mobile 3.14
Stringhe
I letterali di stringa sono racchiusi tra virgolette doppie "
o backticks ``. Ad esempio:
"ciao, mondo" // Stringa racchiusa tra virgolette doppie, supporta caratteri di escape
`ciao, mondo` // Stringa racchiusa tra backticks, mantiene il formato della stringa senza supportare i caratteri di escape
Booleani
Ci sono solo due valori booleani, true
e false
, che rappresentano vero e falso logicamente:
true // Valore booleano true
false // Valore booleano false
Variabili
Expr consente anche la definizione di variabili nell'ambiente, per poi fare riferimento a queste variabili nell'espressione. Ad esempio:
env := map[string]interface{}{
"età": 25,
"nome": "Alice",
}
Quindi nell'espressione, puoi fare riferimento a età
e nome
:
età > 18 // Controlla se l'età è maggiore di 18
nome == "Alice" // Determina se il nome è uguale a "Alice"
4.2 Operatori
Il motore di espressioni Expr supporta vari operatori, inclusi operatori aritmetici, operatori logici, operatori di confronto e operatori di insieme, ecc.
Operator Aritmetici e Logici
Gli operatori aritmetici includono l'addizione (+
), la sottrazione (-
), la moltiplicazione (*
), la divisione (/
) e il modulo (%
). Gli operatori logici includono l'AND logico (&&
), l'OR logico (||
) e il NOT logico (!
), ad esempio:
2 + 2 // Il risultato è 4
7 % 3 // Il risultato è 1
!true // Il risultato è falso
età >= 18 && nome == "Alice" // Verifica se l'età non è inferiore a 18 e se il nome è uguale a "Alice"
Operatori di Confronto
Gli operatori di confronto includono uguale a (==
), diverso da (!=
), minore di (<
), minore o uguale a (<=
), maggiore di (>
) e maggiore o uguale a (>=
), utilizzati per confrontare due valori:
età == 25 // Verifica se l'età è uguale a 25
età != 18 // Verifica se l'età non è uguale a 18
età > 20 // Verifica se l'età è maggiore di 20
Operatori di Insieme
Expr fornisce anche alcuni operatori per lavorare con insiemi, come in
per verificare se un elemento è nell'insieme. Gli insiemi possono essere array, slice o mappe:
"user" in ["user", "admin"] // vero, perché "user" è nell'array
3 in {1: true, 2: false} // falso, perché 3 non è una chiave nella mappa
Ci sono anche alcune funzioni avanzate di operazioni sugli insiemi, come all
, any
, one
e none
, che richiedono l'uso di funzioni anonime (lambda):
all(tweets, {.Len <= 240}) // Verifica se il campo Len di tutti i tweet non supera 240
any(tweets, {.Len > 200}) // Verifica se esiste un campo Len nei tweet che supera 200
Operatore di Membro
Nel linguaggio di espressione Expr, l'operatore di membro ci consente di accedere alle proprietà della struct
nel linguaggio Go. Questa caratteristica consente ad Expr di manipolare direttamente strutture dati complesse, rendendolo molto flessibile e pratico.
L'utilizzo dell'operatore di membro è molto semplice, basta utilizzare l'operatore .
seguito dal nome della proprietà. Per esempio, se abbiamo la seguente struct
:
type Utente struct {
Nome string
Età int
}
È possibile scrivere un'espressione per accedere alla proprietà Nome
della struttura Utente
in questo modo:
env := map[string]interface{}{
"utente": Utente{Nome: "Alice", Età: 25},
}
codice := `utente.Nome`
programma, err := expr.Compile(codice, expr.Env(env))
if err != nil {
panic(err)
}
output, err := expr.Run(programma, env)
if err != nil {
panic(err)
}
fmt.Println(output) // Output: Alice
Gestione dei Valori nulli
Nell'accesso alle proprietà, potresti trovarsi di fronte a situazioni in cui l'oggetto è nil
. Expr fornisce un accesso alle proprietà in modo sicuro, quindi anche se la struct o la proprietà nidificata è nil
, non genererà un errore di runtime.
Usa l'operatore ?.
per fare riferimento alle proprietà. Se l'oggetto è nullo, restituirà null anziché generare un errore.
autore.Utente?.Nome
Espressione equivalente
autore.Utente != nil ? autore.Utente.Nome : nil
L'utilizzo dell'operatore ??
è principalmente per restituire valori predefiniti:
autore.Utente?.Nome ?? "Anonimo"
Espressione equivalente
autore.Utente != nil ? autore.Utente.Nome : "Anonimo"
Operatore Pipe
L'operatore di pipe (|
) in Expr viene utilizzato per passare il risultato di un'espressione come parametro a un'altra espressione. Questo è simile all'operazione di pipe nella shell Unix, che consente di concatenare più moduli funzionali per formare un'elaborazione a pipeline. In Expr, utilizzarlo può creare espressioni più chiare e concise.
Ad esempio, se abbiamo una funzione per ottenere il nome di un utente e un modello per un messaggio di benvenuto:
env := map[string]interface{}{
"user": User{Name: "Bob", Age: 30},
"get_name": func(u User) string { return u.Name },
"greet_msg": "Ciao, %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: Ciao, Bob!
In questo esempio, prima otteniamo il nome dell'utente tramite get_name(user)
, quindi passiamo il nome alla funzione sprintf
usando l'operatore di pipe |
per generare il messaggio di benvenuto finale.
L'utilizzo dell'operatore di pipe può modularizzare il nostro codice, migliorare la riusabilità del codice e rendere le espressioni più leggibili.
4.3 Funzioni
Expr supporta funzioni incorporate e personalizzate, rendendo le espressioni più potenti e flessibili.
Uso di Funzioni Incorporate
Funzioni incorporate come len
, all
, none
, any
, ecc. possono essere utilizzate direttamente nell'espressione.
// Esempio di utilizzo di una funzione incorporata
program, err := expr.Compile(`all(users, {.Age >= 18})`, expr.Env(env))
if err != nil {
panic(err)
}
// Nota: qui l'ambiente deve contenere la variabile users, e ogni utente deve avere la proprietà Age
output, err := expr.Run(program, env)
fmt.Print(output) // Se tutti gli utenti nell'ambiente hanno 18 anni o più, restituirà true
Come Definire e Utilizzare Funzioni Personalizzate
In Expr, è possibile creare funzioni personalizzate passando le definizioni delle funzioni nella mappatura dell'ambiente.
// Esempio di funzione personalizzata
env := map[string]interface{}{
"greet": func(name string) string {
return fmt.Sprintf("Ciao, %s!", name)
},
}
program, err := expr.Compile(`greet("Mondo")`, expr.Env(env))
if err != nil {
panic(err)
}
output, err := expr.Run(program, env)
fmt.Print(output) // Restituisce Ciao, Mondo!
Quando si utilizzano le funzioni in Expr, è possibile modularizzare il proprio codice e incorporare logiche complesse nelle espressioni. Combinando variabili, operatori e funzioni, Expr diventa uno strumento potente e facile da usare. Ricordati sempre di garantire la sicurezza dei tipi durante la creazione dell'ambiente di Expr ed esecuzione delle espressioni.
5. Documentazione delle Funzioni Incorporate
Expr fornisce agli sviluppatori un ricco set di funzioni incorporate per gestire vari scenari complessi. Di seguito, dettaglieremo queste funzioni incorporate e il loro utilizzo.
all
La funzione all
può essere utilizzata per verificare se tutti gli elementi in una collezione soddisfano una data condizione. Prende due parametri: la collezione e l'espressione di condizione.
// Controllo se tutti i tweet hanno una lunghezza del contenuto inferiore a 240
code := `all(tweets, len(.Content) < 240)`
any
Simile a all
, la funzione any
è utilizzata per verificare se qualche elemento in una collezione soddisfa una condizione.
// Controllo se qualche tweet ha una lunghezza del contenuto maggiore di 240
code := `any(tweets, len(.Content) > 240)`
none
La funzione none
viene utilizzata per verificare se nessun elemento in una collezione soddisfa una condizione.
// Assicurarsi che nessun tweet sia ripetuto
code := `none(tweets, .IsRepeated)`
one
La funzione one
viene utilizzata per confermare che solo un elemento in una collezione soddisfi una condizione.
// Controllo se solo un tweet contiene una parola chiave specifica
code := `one(tweets, contains(.Content, "parola chiave"))`
filter
La funzione filter
può filtrare gli elementi della collezione che soddisfano una data condizione.
// Filtrare tutti i tweet contrassegnati come prioritari
code := `filter(tweets, .IsPriority)`
map
La funzione map
viene utilizzata per trasformare gli elementi di una collezione secondo un metodo specificato.
// Formattazione dell'orario di pubblicazione di tutti i tweet
code := `map(tweets, {.PublishTime: Format(.Date)})`
len
La funzione len
è utilizzata per restituire la lunghezza di una collezione o di una stringa.
// Ottenere la lunghezza dello username
code := `len(username)`
contains
La funzione contains
è utilizzata per verificare se una stringa contiene una sottostringa o se una collezione contiene un elemento specifico.
// Verificare se lo username contiene caratteri non validi
code := `contains(username, "caratteri non validi")`
Quelle menzionate sopra sono solo una parte delle funzioni integrate fornite dal motore di espressioni Expr. Con queste potenti funzioni, è possibile gestire i dati e la logica in modo più flessibile ed efficiente. Per un elenco più dettagliato delle funzioni e istruzioni sull'uso, consultare la documentazione ufficiale di Expr.