1. Einführung
Expr ist eine dynamische Konfigurationslösung, die für die Go-Sprache entwickelt wurde und für ihre einfache Syntax und leistungsstarken Leistungsmerkmale bekannt ist. Der Kern des Expr-Ausdrucks-Engines konzentriert sich auf Sicherheit, Geschwindigkeit und Anschaulichkeit und ist somit für Szenarien wie Zugriffskontrolle, Datenfilterung und Ressourcenverwaltung geeignet. Wenn Expr in Go angewendet wird, verbessert es erheblich die Fähigkeit von Anwendungen, dynamische Regeln zu behandeln. Im Gegensatz zu Interpretern oder Skript-Engines in anderen Sprachen verwendet Expr eine statische Typüberprüfung und generiert Bytecode für die Ausführung, um sowohl Leistung als auch Sicherheit zu gewährleisten.
2. Expr installieren
Sie können den Expr-Ausdrucks-Engine mithilfe des Paketverwaltungstools go get
der Go-Sprache installieren:
go get github.com/expr-lang/expr
Mit diesem Befehl werden die Expr-Bibliotheksdateien heruntergeladen und in Ihr Go-Projekt installiert, sodass Sie Expr in Ihrem Go-Code importieren und verwenden können.
3. Schnellstart
3.1 Kompilieren und Ausführen einfacher Ausdrücke
Beginnen wir mit einem einfachen Beispiel: Schreiben eines einfachen Ausdrucks, Kompilieren und dann Ausführen, um das Ergebnis zu erhalten.
package main
import (
"fmt"
"github.com/expr-lang/expr"
)
func main() {
// Kompilieren eines einfachen Additionsausdrucks
program, err := expr.Compile(`2 + 2`)
if err != nil {
panic(err)
}
// Ausführen des kompilierten Ausdrucks, ohne eine Umgebung zu übergeben, da hier keine Variablen benötigt werden
output, err := expr.Run(program, nil)
if err != nil {
panic(err)
}
// Ergebnis ausgeben
fmt.Println(output) // Gibt 4 aus
}
In diesem Beispiel wird der Ausdruck 2 + 2
in ausführbaren Maschinencode kompiliert, der dann ausgeführt wird, um die Ausgabe zu erzeugen.
3.2 Verwenden von Variablenausdrücken
Als nächstes erstellen wir eine Umgebung mit Variablen, schreiben einen Ausdruck, der diese Variablen verwendet, und kompilieren und führen diesen Ausdruck aus.
package main
import (
"fmt"
"github.com/expr-lang/expr"
)
func main() {
// Erstellen einer Umgebung mit Variablen
env := map[string]interface{}{
"foo": 100,
"bar": 200,
}
// Kompilieren eines Ausdrucks, der Variablen aus der Umgebung verwendet
program, err := expr.Compile(`foo + bar`, expr.Env(env))
if err != nil {
panic(err)
}
// Ausführen des Ausdrucks
output, err := expr.Run(program, env)
if err != nil {
panic(err)
}
// Ergebnis ausgeben
fmt.Println(output) // Gibt 300 aus
}
In diesem Beispiel enthält die Umgebung env
die Variablen foo
und bar
. Der Ausdruck foo + bar
leitet die Typen von foo
und bar
während der Kompilierung aus der Umgebung ab und verwendet dann zur Laufzeit die Werte dieser Variablen, um das Ergebnis des Ausdrucks zu bewerten.
4. Expr-Syntax im Detail
4.1 Variablen und Literale
Die Expr-Ausdrucks-Engine kann gebräuchliche Literaldatentypen verarbeiten, einschließlich Zahlen, Zeichenketten und boolescher Werte. Literale sind Datenwerte, die direkt im Code geschrieben sind, wie 42
, "hello"
und true
.
Zahlen
In Expr können Sie direkt Ganzzahlen und Gleitkommazahlen schreiben:
42 // Repräsentiert die Ganzzahl 42
3.14 // Repräsentiert die Gleitkommazahl 3.14
Zeichenketten
Zeichenkettenliterale sind in doppelten Anführungszeichen "
oder Backticks `` eingeschlossen. Zum Beispiel:
"hello, world" // Zeichenkette in doppelten Anführungszeichen, unterstützt Escape-Zeichen
`hello, world` // Zeichenkette in Backticks eingeschlossen, behält das Zeichenkettenformat ohne Unterstützung für Escape-Zeichen bei
Boolesche Werte
Es gibt nur zwei boolesche Werte, true
und false
, die logisches true und false repräsentieren:
true // Boolescher true-Wert
false // Boolescher falscher Wert
Variablen
Expr erlaubt auch die Definition von Variablen in der Umgebung und referenziert diese Variablen dann im Ausdruck. Zum Beispiel:
env := map[string]interface{}{
"age": 25,
"name": "Alice",
}
Dann im Ausdruck können Sie auf age
und name
verweisen:
age > 18 // Überprüfen, ob das Alter größer als 18 ist
name == "Alice" // Feststellen, ob der Name gleich "Alice" ist
4.2 Operatoren
Die Expr-Ausdrucks-Engine unterstützt verschiedene Operatoren, einschließlich arithmetischer Operatoren, logischer Operatoren, Vergleichsoperatoren und Mengenoperatoren usw.
Rechen- und Logikoperatoren
Rechenoperatoren umfassen Addition (+
), Subtraktion (-
), Multiplikation (*
), Division (/
) und Modulo (%
). Logikoperatoren umfassen logisches UND (&&
), logisches ODER (||
) und logisches NICHT (!
), zum Beispiel:
2 + 2 // Ergebnis ist 4
7 % 3 // Ergebnis ist 1
!true // Ergebnis ist false
age >= 18 && name == "Alice" // Überprüfen, ob das Alter nicht weniger als 18 ist und ob der Name gleich "Alice" ist
Vergleichsoperatoren
Vergleichsoperatoren umfassen Gleichheit (==
), Ungleichheit (!=
), kleiner als (<
), kleiner oder gleich (<=
), größer als (>
) und größer oder gleich (>=
), die zum Vergleich von zwei Werten verwendet werden:
age == 25 // Überprüfen, ob das Alter gleich 25 ist
age != 18 // Überprüfen, ob das Alter ungleich 18 ist
age > 20 // Überprüfen, ob das Alter größer als 20 ist
Mengenoperatoren
Expr bietet auch einige Operatoren zum Arbeiten mit Mengen, wie z.B. in
, um zu überprüfen, ob ein Element in der Menge enthalten ist. Mengen können Arrays, Slices oder Maps sein:
"user" in ["user", "admin"] // true, weil "user" im Array enthalten ist
3 in {1: true, 2: false} // false, weil 3 kein Schlüssel in der Map ist
Es gibt auch einige fortgeschrittene Mengenoperationsfunktionen wie all
, any
, one
und none
, die die Verwendung anonymer Funktionen (Lambda) erfordern:
all(tweets, {.Len <= 240}) // Überprüfen, ob das Len-Feld aller Tweets nicht mehr als 240 Zeichen enthält
any(tweets, {.Len > 200}) // Überprüfen, ob in den Tweets ein Len-Feld vorhanden ist, das mehr als 200 Zeichen enthält
Member-Operator
Im Expr-Ausdrucks-Sprache ermöglicht uns der Member-Operator den Zugriff auf die Eigenschaften des struct
in der Go-Sprache. Diese Funktion ermöglicht Expr die direkte Manipulation von komplexen Datenstrukturen, was sehr flexibel und praktisch ist.
Die Verwendung des Member-Operators ist sehr einfach, verwenden Sie einfach den .
-Operator gefolgt vom Eigenschaftsnamen. Wenn wir beispielsweise die folgende struct
haben:
type User struct {
Name string
Age int
}
Können Sie einen Ausdruck schreiben, um die Name
-Eigenschaft der User
-Struktur wie folgt abzurufen:
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) // Ergebnis: Alice
Umgang mit Nullwerten
Beim Zugriff auf Eigenschaften können Sie auf Situationen stoßen, in denen das Objekt nil
ist. Expr bietet sicheren Eigenschaftszugriff, sodass selbst wenn das struct
oder die verschachtelte Eigenschaft nil
ist, kein Laufzeit-Panicfehler ausgelöst wird.
Verwenden Sie den ?.
-Operator, um auf Eigenschaften zu verweisen. Wenn das Objekt nil
ist, wird es anstelle eines Fehlers nil
zurückgeben.
author.User?.Name
Äquivalenter Ausdruck
author.User != nil ? author.User.Name : nil
Die Verwendung des ??
-Operators dient hauptsächlich zur Rückgabe von Standardwerten:
author.User?.Name ?? "Anonymous"
Äquivalenter Ausdruck
author.User != nil ? author.User.Name : "Anonymous"
Pipe-Operator
Der Pipe-Operator (|
) in Expr wird verwendet, um das Ergebnis eines Ausdrucks als Parameter an einen anderen Ausdruck zu übergeben. Dies ähnelt der Pipe-Operation in der Unix-Shell, die es ermöglicht, mehrere Funktionsmodule miteinander zu verketten, um eine Verarbeitungspipeline zu bilden. In Expr kann seine Verwendung zu klareren und prägnanteren Ausdrücken führen.
Wenn wir beispielsweise eine Funktion haben, um den Namen eines Benutzers und eine Vorlage für eine Begrüßungsnachricht zu erhalten:
env := map[string]interface{}{
"user": User{Name: "Bob", Age: 30},
"get_name": func(u User) string { return u.Name },
"greet_msg": "Hallo, %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) // Ausgabe: Hallo, Bob!
In diesem Beispiel erhalten wir zunächst den Namen des Benutzers durch get_name(user)
und geben dann den Namen mit dem Pipe-Operator |
an die Funktion sprintf
weiter, um die endgültige Begrüßungsnachricht zu generieren.
Die Verwendung des Pipe-Operators kann unseren Code modularisieren, die Wiederverwendbarkeit des Codes verbessern und die Ausdrücke lesbarer machen.
4.3 Funktionen
Expr unterstützt eingebaute Funktionen und benutzerdefinierte Funktionen, um Ausdrücke leistungsstärker und flexibler zu gestalten.
Verwendung von eingebauten Funktionen
Eingebaute Funktionen wie len
, all
, none
, any
, usw. können direkt im Ausdruck verwendet werden.
// Beispiel zur Verwendung einer eingebauten Funktion
program, err := expr.Compile(`all(users, {.Age >= 18})`, expr.Env(env))
if err != nil {
panic(err)
}
// Hinweis: Hier muss env die Variable users enthalten und jeder Benutzer muss die Eigenschaft Age haben
output, err := expr.Run(program, env)
fmt.Print(output) // Wenn alle Benutzer in env 18 Jahre oder älter sind, wird true zurückgegeben
Definition und Verwendung benutzerdefinierter Funktionen
In Expr können benutzerdefinierte Funktionen erstellt werden, indem Funktionsdefinitionen in die Umgebungsabbildung übergeben werden.
// Beispiel einer benutzerdefinierten Funktion
env := map[string]interface{}{
"greet": func(name string) string {
return fmt.Sprintf("Hallo, %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) // Gibt Hallo, World! zurück
Bei der Verwendung von Funktionen in Expr können Sie Ihren Code modularisieren und komplexe Logik in die Ausdrücke integrieren. Durch die Kombination von Variablen, Operatoren und Funktionen wird Expr zu einem leistungsstarken und einfach zu verwendenden Werkzeug. Denken Sie immer daran, die Typsicherheit beim Erstellen der Expr-Umgebung und Ausführen von Ausdrücken zu gewährleisten.
5. Dokumentation zu eingebauten Funktionen
Die Expr-Ausdrucks-Engine stellt Entwicklern eine Vielzahl von eingebauten Funktionen zur Verfügung, um verschiedene komplexe Szenarien zu bewältigen. Im Folgenden werden diese eingebauten Funktionen und ihre Verwendung im Detail erläutert.
all
Die Funktion all
kann verwendet werden, um zu überprüfen, ob alle Elemente in einer Sammlung eine bestimmte Bedingung erfüllen. Sie nimmt zwei Parameter an: die Sammlung und den Bedingungsausdruck.
// Überprüfen, ob alle Tweets eine Inhaltslänge von weniger als 240 haben
code := `all(tweets, len(.Content) < 240)`
any
Ähnlich wie all
wird die Funktion any
verwendet, um zu überprüfen, ob ein Element in einer Sammlung eine Bedingung erfüllt.
// Überprüfen, ob ein Tweet eine Inhaltslänge von mehr als 240 hat
code := `any(tweets, len(.Content) > 240)`
none
Die Funktion none
wird verwendet, um zu überprüfen, ob kein Element in einer Sammlung eine Bedingung erfüllt.
// Sicherstellen, dass keine Tweets wiederholt werden
code := `none(tweets, .IsRepeated)`
one
Die Funktion one
wird verwendet, um sicherzustellen, dass nur ein Element in einer Sammlung eine Bedingung erfüllt.
// Überprüfen, ob nur ein Tweet ein bestimmtes Schlüsselwort enthält
code := `one(tweets, contains(.Content, "keyword"))`
filter
Die Funktion filter
kann Elemente einer Sammlung filtern, die eine bestimmte Bedingung erfüllen.
// Herausfiltern aller Tweets, die als Priorität markiert sind
code := `filter(tweets, .IsPriority)`
map
Die Funktion map
wird verwendet, um Elemente einer Sammlung gemäß einer bestimmten Methode zu transformieren.
// Formatieren der Veröffentlichungszeit aller Tweets
code := `map(tweets, {.PublishTime: Format(.Date)})`
len
Die Funktion len
wird verwendet, um die Länge einer Sammlung oder eines Strings zurückzugeben.
// Die Länge des Benutzernamens abrufen
code := `len(username)`
contains
Die Funktion contains
wird verwendet, um zu überprüfen, ob ein String einen Teilstring enthält oder ob eine Sammlung ein bestimmtes Element enthält.
// Überprüfen, ob der Benutzername illegale Zeichen enthält
code := `contains(username, "illegale Zeichen")`
Die oben genannten Funktionen sind nur ein Teil der integrierten Funktionen, die von der Expr-Ausdruckssprache bereitgestellt werden. Mit diesen leistungsstarken Funktionen können Sie Daten und Logik flexibler und effizienter bearbeiten. Für eine ausführlichere Liste von Funktionen und Anwendungshinweisen verweisen wir auf die offizielle Expr-Dokumentation.