Heim >Backend-Entwicklung >Python-Tutorial >Neugestaltung einer Rule-Engine-DSL
Vor ein paar Jahren habe ich eine domänenspezifische Sprache (DSL) neu implementiert, die ursprünglich für die Regel-Engine bei der Arbeit entwickelt wurde. Die Neuimplementierung des Spielzeugs wurde in Javascript (ursprünglich in Python) geschrieben und auf GitHub veröffentlicht. Ich hatte nicht erwartet, dass es viel bewirken würde, da es speziell für einen ganz bestimmten Anwendungsfall entwickelt wurde, den ich nicht preisgeben sollte.
Ein etwas süßes Bild, erbrochen von Bing Copilot
Das Hauptziel des Designs war etwas, das leicht serialisierbar ist. Die Turing-Vollständigkeit war kein Problem, da ich es nur für zwei Dinge brauchte:
Ich habe zunächst damit begonnen, anonyme Funktionen in Python zu schreiben. Als ich jedoch versuchte, die Arbeit auf eine Reihe von Threads/Prozessen zu verteilen, beschwerte sich der Interpreter, dass Lambdas nicht serialisierbar seien. Damals brauchte ich die Logik, um außerhalb des Hauptcodes zu leben, also erstellte ich schließlich das DSL für diesen Zweck.
Das erste, was mir in den Sinn kam, war Lisp, da mir gefällt, wie der Code Arrays/Listen ähnelt. Die Ähnlichkeit ist gut, da ich die Konfiguration bereits in YAML gespeichert habe. Daher muss ich mir keine Gedanken über die Schaffung einer neuen Art der Darstellung der Logik machen.
Das Speichern der Sprache als Liste bringt einen alternativen Vorteil mit sich: Ich musste keinen Parser von Grund auf erstellen, d. h. es war nicht nötig, eine lexikalische Analyse (Lexer) zu tokenisieren/durchzuführen. Mit anderen Worten: Der Autor ist der Lexer. Zur Implementierung musste ich lediglich die Eingabeliste nehmen, herausfinden, ob es sich um ein Programm handelt (wir nennen es eine Regel), und es in Bezug auf einen Kontext ausführen.
const schema = ["condition.Equal", ["basic.Field", "foo"], ["basic.Field", "bar"]]; // returns a function that checks if context.foo === context.bar const rule = ruler.parse(rule) const context = {foo: "meow", bar: "woof"}; rule(context) // returns false
Alles hat gut und wie erwartet funktioniert. Dann bin ich vor ein paar Tagen auf einen Artikel zur Implementierung eines Schemas in Python gestoßen. Ich habe den Artikel wahrscheinlich schon einmal gelesen, als ich viel Zeit damit verbracht habe, Clojure zu lernen. Dieses Mal habe ich mich jedoch entschieden, die Bibliothek noch einmal von Grund auf neu zu implementieren, wieder mit Python.
Dieses Mal musste ich den Lexer also selbst tokenisieren und ausführen. Wenn ich mich nur mit Zahlen beschäftige, ist alles einfach, aber wenn es um Zeichenfolgen geht, wird es komplizierter. Ich folgte einem anderen Tutorial und entdeckte das Make-a-Lisp-Projekt wieder. Schließlich gab ich auf und benutzte den von hy-lang bereitgestellten Lexer.
Der Lexer nimmt einen S-Ausdruck und gibt eine Struktur zurück, die einem abstrakten Syntaxbaum ähnelt. Von dort aus baue ich meinen Parser, indem ich den Baum durchlaufe und die Regel als Abschluss zurückgebe, der ein Wörterbuch als Kontext verwendet.
const schema = ["condition.Equal", ["basic.Field", "foo"], ["basic.Field", "bar"]]; // returns a function that checks if context.foo === context.bar const rule = ruler.parse(rule) const context = {foo: "meow", bar: "woof"}; rule(context) // returns false
Die neue Implementierung bringt keinen wirklichen Vorteil, da ich den Job schon seit einigen Jahren aufgegeben habe. Die Implementierung, die ich hinterlassen habe, läuft wahrscheinlich bis heute noch gut (besser nach so vielen Iterationen, die dazu geführt haben). Allerdings lerne ich auf dieser Reise immer noch ein oder zwei Dinge. Wenn Sie das interessant finden, schauen Sie sich gerne Javascript (wo es ein Regelschema im Array erwartet) oder die neue Python-Version (S-Ausdruck) an.
Das obige ist der detaillierte Inhalt vonNeugestaltung einer Rule-Engine-DSL. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!