rbs – Rule Based System (Regel-Engine)
Zusammenfassung
rbs ist eine kleine C-Regel-Engine mit einer Ausführungs-Semantik
(rbs_step): Gegen dieselbe Faktenbasis werden alle Regeln (Guards) und
Effekte (Memory-Operationen) ausgewertet und am Schritt-Ende atomar
committet. Zusammen mit sm (bzw. der Brücke rbs_sm) entsteht ein
deterministischer, vollständig überprüfbarer Zustandsautomat. Das Wissen
liegt deklarativ in Daten, nicht im Code.
rbs ist als Bibliothek aufgebaut (STATIC, Konvention der
libraries/-Module); die Beispiel-Anwendung, die die Bibliothek mit
Lebenszyklus-Callbacks zeigt, liegt im separaten Projekt rbs_demo.
Einordnung
| Schicht | Ort | Aufgabe |
|---|---|---|
rbs | libraries/rbs/rbs.c | Regel-Engine: Faktenbasis, Memory, Regeln, Effekte |
rbs_sm | libraries/rbs/rbs_sm.c | Brücke: RBS-Schritt + Routing auf Handler-Slots, Schritt-Callback |
sm | libraries/sm | minimal-threaded State Machine (Handler-Schleife), inkl. Lebenszyklus-Callbacks |
threading | libraries/threading | Thread-Erzeugung/-Joining für sm |
rbs_demo | apps/rbs_demo (eigenes Repo) | Beispiel-App: Regeln, Effekte, Slots, Lebenszyklus |
Datenmodell
- Faktenbasis:
int32_t[], ein Slot pro Token. Signum-Kodierung:+X= aktiv,N_X(negierte Form) = „nicht (mehr) aktiv”. Initialisierung setzt alle Slots aufN_X. - Memory:
double[]-Wertebereich, über Value-Enum indiziert. - Terme (
struct rbs_term): Faktenterm (nurfact_enum) oder Wertvergleich (memory[value_enum] op operand), kombiniert als UND-Kette mitZERO-Terminator. - Regel (
struct rbs_rule):if_terms→then_facts(abzuleitende Fakten) und optionalelse_facts(gesetzt, wenn derif-Zweig nicht zutrifft; typisch der deklarative Reset, z. B.N_WET). Einthen_facts-Eintrag ist ein bewachtes Setzen (+X). - Effekt (
struct rbs_effect): Trigger-Fakt aktiv → Memory-Operation (ADD/SUB/MUL/DIV) aufvalue_enum. Der Trigger bleibt aktiv (zustandsgetriggert, kein Auto-Konsum); zurückgesetzt wird er nur durch eine Regel oder eine externe Faktenquelle.
Schritt-Semantik (Kern)
- Alle Regeln gegen die aktuelle Faktenbasis auswerten – kein Regelfakt ist im laufenden Schritt für andere Regeln sichtbar.
- Alle Effekte gegen denselben Memory-Snapshot berechnen – keine Kette.
- Commit am Schritt-Ende: abgeleitete Fakten (
then/else) setzen, Memory schreiben. Wirkt ab dem nächsten Schritt.
Fakten persistieren – sie existieren, bis eine Regel (else_facts mit
N_X) oder eine externe Faktenquelle (App-Handler, z. B. Systemzeit) sie
explizit zurücksetzt. Es gibt kein automatisches Konsumieren; Effekte feuern,
solange ihr Trigger aktiv ist. Beendigung ist Programmlogik der App.
Konsequenz: Die Signum-Kodierung ist bereits die Transition (Diff); es braucht
keinen zweiten Faktenpuffer. next = step(current) ist eine reine Funktion;
beliebige Umgebungs-Fakten dürfen nur zwischen den Schritten eingespeist
werden (geschlossene Welt, keine asynchronen Effekte).
API (Auszug)
struct rbs { int32_t* facts; uint32_t token_count; memory_t memory; uint32_t value_count; };
void rbs_step(struct rbs* rbs,
const rbs_rule_t rules, size_t rule_count,
const rbs_effect_t effects, size_t effect_count);
int32_t* rbs_create_facts_buffer(uint32_t token_count);
bool rbs_is_fact(int32_t* facts, uint32_t token_count, int32_t fact);
void rbs_set_fact(int32_t* facts, uint32_t token_count, int32_t fact);
bool rbs_compare(memory_t memory, uint32_t value_count, int32_t value, enum operation op, double operand);struct rbs trägt die Puffergrößen mit; die Fakten-/Vergleichsfunktionen
verwerfen Zugriffe mit magnitude >= token_count bzw.
value_enum >= value_count (Bounds-Guards, kein OOB).
signedness: Fakten-Vergleich erfolgt auf das Vorzeichen (N_X-Form ist
falsch, +X wahr). Das ist „Constraints statt Konventionen”: rbs_step ist
der einzige Mutationspfad.
rbs_sm – Brücke zur State Machine
rbs_sm_slot { .fact, .handler }: Nach dem RBS-Schritt wird der erste
aktive Slot gesucht und dessen Handler geroutet. Der Handler ist eine
externe Faktenquelle (z. B. Systemzeit/Wettersensor), die Fakten
hinzufügen oder zurücksetzen darf; er „konsumiert” keine Fakten. Beendigung =
kein Markerslot mehr gesetzt (bzw. Handler liefert false) – Programmlogik
der App. on_step (optional) wird je Schritt mit der Schrittnummer
aufgerufen; ticks zählt die Schritte.
typedef struct rbs_sm_slot { int32_t fact; sm_state_handler_t handler; } *rbs_sm_slot_t;
struct rbs_sm {
struct rbs* rbs;
rbs_rule_t rules; size_t rule_count;
rbs_effect_t effects; size_t effect_count;
rbs_sm_slot_t slots; size_t slot_count;
void (*on_step)(struct rbs_sm* fsm, uint32_t tick); /* optional */
uint32_t ticks;
};
void rbs_sm_init(rbs_sm_t fsm, struct rbs* rbs,
const rbs_rule_t rules, size_t rule_count,
const rbs_effect_t effects, size_t effect_count,
const rbs_sm_slot_t slots, size_t slot_count);
bool rbs_sm_advance(rbs_sm_t fsm, sm_state_t current);
void rbs_sm_run(rbs_sm_t fsm); /* blockierend bis Beendigung */Der Lebenszyklus der App-FSM ist nicht mehr Teil von struct rbs_sm:
Er liegt in den sm-Callbacks sm_on_start/sm_on_stop
(Konstruktor/Destruktor, weak, überschreibbar in der App) — siehe
rbs_demo.
Nutzung
#include <rbs/rbs.h>
#include <rbs/rbs_sm.h>
#include <sm/sm.h>
struct rbs_term if_adult[] = { { .comparison = true, .value_enum = AGE, .op = GT, .operand = 18 }, { .comparison = false, .fact_enum = ZERO } };
enum token then_adult[] = { ADULT, PAY, ZERO };
struct rbs_effect effects[] = { { .trigger_fact_enum = PAY, .value_enum = MONEY, .op = SUB, .operand = 10 } };
struct rbs rbs = { .facts = fact_buffer, .token_count = TOKEN_COUNT,
.memory = mel, .value_count = VALUE_COUNT };
rbs_step(&rbs, rules, rule_count, effects, effect_count);Build & Test
cmake -S . -B build && cmake --build build && ctest --test-dir buildStruktur wie sm/logging: rbs.c/rbs_sm.c im Repo-Root, Header in
rbs/ (rbs/rbs.h, rbs/rbs_sm.h); Einbindung per
add_subdirectory(../../libraries/rbs …) mit PUBLIC-Links auf sm, logging,
m (api/threading transitiv).
Quellen
- Repo: https://github.com/deppenkaiser/rbs (public, Apache-2.0)
- Quellcode:
libraries/rbs/rbs.c,libraries/rbs/rbs_sm.c,libraries/rbs/rbs/{rbs.h,rbs_sm.h},libraries/rbs/test/ - Beispiel-App: https://github.com/deppenkaiser/rbs_demo (public, Apache-2.0)
- Tests:
ctestim Build-Ordner (rbs_test,rbs_effects_test,rbs_sm_test,rbs_else_test)
Verwandte Artikel
- sm – minimal threaded State Machine
- rbs_demo – Beispiel-App
- rbs & sm – Regel-Engine als Zustandsautomat – Gesamtkonzept und Design-Begründung („Constraints statt Konventionen”)