1. Introduction
Expr est une solution de configuration dynamique conçue pour le langage Go, reconnue pour sa syntaxe simple et ses performances puissantes. Le cœur du moteur d'expression Expr se concentre sur la sécurité, la vitesse et l'intuitivité, le rendant adapté à des scénarios tels que le contrôle d'accès, le filtrage de données et la gestion des ressources. Lorsqu'il est appliqué à Go, Expr améliore considérablement la capacité des applications à gérer des règles dynamiques. Contrairement aux interprètes ou aux moteurs de script dans d'autres langages, Expr adopte une vérification de type statique et génère un bytecode pour l'exécution, garantissant à la fois des performances et une sécurité.
2. Installation de Expr
Vous pouvez installer le moteur d'expression Expr en utilisant l'outil de gestion de packages du langage Go go get
:
go get github.com/expr-lang/expr
Cette commande téléchargera les fichiers de la bibliothèque Expr et les installera dans votre projet Go, vous permettant d'importer et d'utiliser Expr dans votre code Go.
3. Démarrage rapide
3.1 Compilation et exécution des expressions de base
Commençons par un exemple simple : écrire une expression simple, la compiler, puis l'exécuter pour obtenir le résultat.
package main
import (
"fmt"
"github.com/expr-lang/expr"
)
func main() {
// Compilation d'une addition de base
program, err := expr.Compile(`2 + 2`)
if err != nil {
panic(err)
}
// Exécution de l'expression compilée sans passer d'environnement, car aucune variable n'est nécessaire ici
output, err := expr.Run(program, nil)
if err != nil {
panic(err)
}
// Affichage du résultat
fmt.Println(output) // Affiche 4
}
Dans cet exemple, l'expression 2 + 2
est compilée en bytecode exécutable, qui est ensuite exécuté pour produire la sortie.
3.2 Utilisation d'expressions avec des variables
Ensuite, nous allons créer un environnement contenant des variables, écrire une expression qui utilise ces variables, la compiler et exécuter cette expression.
package main
import (
"fmt"
"github.com/expr-lang/expr"
)
func main() {
// Création d'un environnement avec des variables
env := map[string]interface{}{
"foo": 100,
"bar": 200,
}
// Compilation d'une expression utilisant des variables de l'environnement
program, err := expr.Compile(`foo + bar`, expr.Env(env))
if err != nil {
panic(err)
}
// Exécution de l'expression
output, err := expr.Run(program, env)
if err != nil {
panic(err)
}
// Affichage du résultat
fmt.Println(output) // Affiche 300
}
Dans cet exemple, l'environnement env
contient les variables foo
et bar
. L'expression foo + bar
déduit les types de foo
et bar
à partir de l'environnement lors de la compilation, et utilise les valeurs de ces variables à l'exécution pour évaluer le résultat de l'expression.
4. Syntaxe de Expr en détail
4.1 Variables et littéraux
Le moteur d'expression Expr peut manipuler des littéraux de types de données courants, y compris des nombres, des chaînes de caractères et des valeurs booléennes. Les littéraux sont des valeurs de données directement écrites dans le code, telles que 42
, "hello"
et true
.
Nombres
En Expr, vous pouvez écrire directement des entiers et des nombres à virgule flottante :
42 // Représente l'entier 42
3.14 // Représente le nombre à virgule flottante 3.14
Chaînes de caractères
Les littéraux de chaînes sont encadrés de guillemets doubles "
ou d'accent graves ``. Par exemple :
"hello, world" // Chaîne encadrée de guillemets doubles, prend en charge les caractères d'échappement
`hello, world` // Chaîne encadrée d'accent graves, maintient le format de la chaîne sans prendre en charge les caractères d'échappement
Booléens
Il n'y a que deux valeurs booléennes, true
and false
, représentant vrai et faux logiques :
true // Valeur booléenne vrai
false // Valeur booléenne faux
Variables
Expr permet également la définition de variables dans l'environnement, puis fait référence à ces variables dans l'expression. Par exemple :
env := map[string]interface{}{
"age": 25,
"name": "Alice",
}
Ensuite, dans l'expression, vous pouvez faire référence à age
et name
:
age > 18 // Vérifie si l'âge est supérieur à 18
name == "Alice" // Détermine si le nom est égal à "Alice"
4.2 Opérateurs
Le moteur d'expression Expr prend en charge divers opérateurs, notamment des opérateurs arithmétiques, logiques, de comparaison et d'ensemble, etc.
Opérateurs arithmétiques et logiques
Les opérateurs arithmétiques incluent l'addition (+
), la soustraction (-
), la multiplication (*
), la division (/
) et le modulo (%
). Les opérateurs logiques incluent le ET logique (&&
), le OU logique (||
) et le NON logique (!
), par exemple :
2 + 2 // Le résultat est 4
7 % 3 // Le résultat est 1
!true // Le résultat est faux
age >= 18 && name == "Alice" // Vérifie si l'âge n'est pas inférieur à 18 et si le nom est égal à "Alice"
Opérateurs de comparaison
Les opérateurs de comparaison incluent égal à (==
), différent de (!=
), inférieur à (<
), inférieur ou égal à (<=
), supérieur à (>
) et supérieur ou égal à (>=
), utilisés pour comparer deux valeurs :
age == 25 // Vérifie si l'âge est égal à 25
age != 18 // Vérifie si l'âge n'est pas égal à 18
age > 20 // Vérifie si l'âge est supérieur à 20
Opérateurs d'ensemble
Expr fournit également certains opérateurs pour travailler avec des ensembles, tels que in
pour vérifier si un élément est dans l'ensemble. Les ensembles peuvent être des tableaux, des tranches ou des cartes :
"user" in ["user", "admin"] // vrai, car "user" est dans le tableau
3 in {1: true, 2: false} // faux, car 3 n'est pas une clé dans la carte
Il existe également quelques fonctions avancées d'opération sur les ensembles, telles que all
, any
, one
et none
, qui nécessitent l'utilisation de fonctions anonymes (lambda) :
all(tweets, {.Len <= 240}) // Vérifie si le champ Len de tous les tweets ne dépasse pas 240
any(tweets, {.Len > 200}) // Vérifie s'il existe un champ Len dans les tweets qui dépasse 200
Opérateur de membre
Dans le langage d'expression Expr, l'opérateur de membre nous permet d'accéder aux propriétés de la struct
en langage Go. Cette fonctionnalité permet à Expr de manipuler directement des structures de données complexes, le rendant très flexible et pratique.
L'utilisation de l'opérateur de membre est très simple, il suffit d'utiliser l'opérateur .
suivi du nom de la propriété. Par exemple, si nous avons la struct
suivante :
type User struct {
Name string
Age int
}
Vous pouvez écrire une expression pour accéder à la propriété Name
de la structure User
comme ceci :
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) // Résultat : Alice
Gestion des valeurs nulles
Lors de l'accès aux propriétés, vous pouvez rencontrer des situations où l'objet est nil
. Expr fournit un accès sécurisé aux propriétés, donc même si la structure ou la propriété imbriquée est nil
, cela ne provoquera pas d'erreur de panique à l'exécution.
Utilisez l'opérateur ?.
pour référencer les propriétés. Si l'objet est nul, il renverra nul au lieu de déclencher une erreur.
author.User?.Name
Expression équivalente
author.User != nil ? author.User.Name : nil
L'utilisation de l'opérateur ??
est principalement pour retourner des valeurs par défaut :
author.User?.Name ?? "Anonyme"
Expression équivalente
author.User != nil ? author.User.Name : "Anonyme"
Opérateur de tube
L'opérateur de tube (|
) dans Expr est utilisé pour passer le résultat d'une expression en tant que paramètre à une autre expression. Cela est similaire à l'opération de tube dans le shell Unix, permettant à plusieurs modules fonctionnels d'être enchaînés pour former un pipeline de traitement. Dans Expr, son utilisation peut créer des expressions plus claires et concises.
Par exemple, si nous avons une fonction pour obtenir le nom d'un utilisateur et un modèle pour un message de bienvenue :
env := map[string]interface{}{
"user": User{Name: "Bob", Age: 30},
"get_name": func(u User) string { return u.Name },
"greet_msg": "Bonjour, %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) // Sortie : Bonjour, Bob!
Dans cet exemple, nous obtenons d'abord le nom de l'utilisateur via get_name(user)
, puis passons le nom à la fonction sprintf
en utilisant l'opérateur de tube |
pour générer le message de bienvenue final.
L'utilisation de l'opérateur de tube peut modulariser notre code, améliorer la réutilisabilité du code et rendre les expressions plus lisibles.
4.3 Fonctions
Expr prend en charge les fonctions intégrées et les fonctions personnalisées, ce qui rend les expressions plus puissantes et flexibles.
Utilisation des fonctions intégrées
Des fonctions intégrées telles que len
, all
, none
, any
, etc. peuvent être utilisées directement dans l'expression.
// Exemple d'utilisation d'une fonction intégrée
program, err := expr.Compile(`all(users, {.Age >= 18})`, expr.Env(env))
if err != nil {
panic(err)
}
// Remarque : ici, l'environnement doit contenir la variable users, et chaque utilisateur doit avoir la propriété Age
output, err := expr.Run(program, env)
fmt.Print(output) // Si tous les utilisateurs dans l'environnement ont 18 ans ou plus, cela renverra vrai
Comment définir et utiliser des fonctions personnalisées
Dans Expr, vous pouvez créer des fonctions personnalisées en passant des définitions de fonctions dans la cartographie de l'environnement.
// Exemple de fonction personnalisée
env := map[string]interface{}{
"greet": func(name string) string {
return fmt.Sprintf("Bonjour, %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) // Renvoie Bonjour, World!
Lors de l'utilisation de fonctions dans Expr, vous pouvez modulariser votre code et incorporer une logique complexe dans les expressions. En combinant des variables, des opérateurs et des fonctions, Expr devient un outil puissant et facile à utiliser. N'oubliez pas de toujours garantir la sécurité des types lors de la construction de l'environnement Expr et de l'exécution des expressions.
5. Documentation des fonctions intégrées
Le moteur d'expression Expr fournit aux développeurs un ensemble riche de fonctions intégrées pour gérer divers scénarios complexes. Ci-dessous, nous détaillerons ces fonctions intégrées et leur utilisation.
all
La fonction all
peut être utilisée pour vérifier si tous les éléments d'une collection satisfont une condition donnée. Elle prend deux paramètres : la collection et l'expression de condition.
// Vérifier si tous les tweets ont une longueur de contenu inférieure à 240
code := `all(tweets, len(.Content) < 240)`
any
Similaire à all
, la fonction any
est utilisée pour vérifier si un élément quelconque dans une collection satisfait une condition.
// Vérifier si un tweet a une longueur de contenu supérieure à 240
code := `any(tweets, len(.Content) > 240)`
none
La fonction none
est utilisée pour vérifier si aucun élément dans une collection ne satisfait une condition.
// S'assurer qu'aucun tweet n'est répété
code := `none(tweets, .IsRepeated)`
one
La fonction one
est utilisée pour confirmer que seul un élément dans une collection satisfait une condition.
// Vérifier si un seul tweet contient un mot-clé spécifique
code := `one(tweets, contains(.Content, "mot-clé"))`
filter
La fonction filter
peut filtrer les éléments de collection qui satisfont une condition donnée.
// Filtrer tous les tweets marqués comme prioritaires
code := `filter(tweets, .IsPriority)`
map
La fonction map
est utilisée pour transformer les éléments d'une collection selon une méthode spécifiée.
// Formater l'heure de publication de tous les tweets
code := `map(tweets, {.PublishTime: Format(.Date)})`
len
La fonction len
est utilisée pour retourner la longueur d'une collection ou d'une chaîne de caractères.
// Obtenir la longueur du nom d'utilisateur
code := `len(username)`
contains
La fonction contains
est utilisée pour vérifier si une chaîne de caractères contient une sous-chaîne de caractères ou si une collection contient un élément spécifique.
// Vérifier si le nom d'utilisateur contient des caractères illégaux
code := `contains(username, "caractères illégaux")`
Ceux mentionnés ci-dessus ne sont qu'une partie des fonctions intégrées fournies par le moteur d'expression Expr. Avec ces fonctions puissantes, vous pouvez manipuler les données et la logique de manière plus flexible et efficace. Pour une liste plus détaillée des fonctions et des instructions d'utilisation, veuillez vous référer à la documentation officielle d'Expr.