Created
December 23, 2025 11:28
-
-
Save K4L1Ma/32b3306237033538f8b2d467c59981ce to your computer and use it in GitHub Desktop.
CEL Guard Implementation for Petri Net Tokens in Go
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| package main | |
| import ( | |
| "fmt" | |
| "sync" | |
| "github.com/google/cel-go/cel" | |
| ) | |
| type TokenID struct { | |
| value string | |
| } | |
| type Token struct { | |
| id TokenID | |
| data map[string]any | |
| } | |
| type Tokens struct { | |
| list []*Token | |
| mu sync.RWMutex | |
| } | |
| func (ts *Tokens) Add(tokens ...*Token) { | |
| ts.mu.Lock() | |
| defer ts.mu.Unlock() | |
| ts.list = append(ts.list, tokens...) | |
| } | |
| func (ts *Tokens) Take(n int) (*Tokens, error) { | |
| ts.mu.Lock() | |
| defer ts.mu.Unlock() | |
| if len(ts.list) < n { | |
| return &Tokens{}, fmt.Errorf("not enough tokens") | |
| } | |
| taken := ts.list[:n] | |
| ts.list = ts.list[n:] | |
| return &Tokens{list: taken}, nil | |
| } | |
| func (ts *Tokens) Count() int { | |
| ts.mu.RLock() | |
| defer ts.mu.RUnlock() | |
| return len(ts.list) | |
| } | |
| func(ts *Tokens) FilterBy(f func(*Token) bool) func(yield func(*Token) bool) { | |
| return func(yield func(*Token) bool) { | |
| for _, token := range ts.list { | |
| if f(token) && !yield(token) { | |
| return | |
| } | |
| } | |
| } | |
| } | |
| func (ts *Tokens) Iterate(yield func(*Token) bool) { | |
| for _, t := range ts.list { | |
| if !yield(t) { | |
| return | |
| } | |
| } | |
| } | |
| func NewTokenList() *Tokens { | |
| return &Tokens{} | |
| } | |
| func TokensList(ts ...*Token) *Tokens { | |
| return &Tokens{ | |
| list: ts, | |
| } | |
| } | |
| type PlaceID = TokenID | |
| type Place struct { | |
| id PlaceID | |
| tokens Tokens | |
| } | |
| type CELGuard struct { | |
| script string `json:"script"` | |
| program cel.Program `json:"-"` | |
| } | |
| func (r CELGuard) Script() string { | |
| return r.script | |
| } | |
| func NewCELGuard(script string) (*CELGuard, error) { | |
| env, err := createCELEnvironment() | |
| if err != nil { | |
| return nil, err | |
| } | |
| program, err := compileScript(env, script) | |
| if err != nil { | |
| return nil, err | |
| } | |
| return &CELGuard{ | |
| script: script, | |
| program: program, | |
| }, nil | |
| } | |
| func createCELEnvironment() (*cel.Env, error) { | |
| return cel.NewEnv( | |
| cel.Variable("id", cel.StringType), | |
| cel.Variable("data", cel.MapType(cel.StringType, cel.DynType)), | |
| ) | |
| } | |
| func compileScript(env *cel.Env, script string) (cel.Program, error) { | |
| ast, issues := env.Compile(script) | |
| if issues != nil && issues.Err() != nil { | |
| return nil, fmt.Errorf("CEL compilation error: %w", issues.Err()) | |
| } | |
| program, err := env.Program(ast) | |
| if err != nil { | |
| return nil, fmt.Errorf("failed to create CEL program: %w", err) | |
| } | |
| return program, nil | |
| } | |
| func (r *CELGuard) Guard(token *Token) bool { | |
| input := buildInputFromToken(token) | |
| result, err := evaluateProgram(r.program, input) | |
| if err != nil { | |
| return false | |
| } | |
| return result | |
| } | |
| func buildInputFromToken(token *Token) map[string]interface{} { | |
| return map[string]interface{}{ | |
| "id": token.id.value, | |
| "data": token.data, | |
| } | |
| } | |
| func evaluateProgram(program cel.Program, input map[string]interface{}) (bool, error) { | |
| out, _, err := program.Eval(input) | |
| if err != nil { | |
| return false, err | |
| } | |
| result, ok := out.Value().(bool) | |
| if !ok { | |
| return false, fmt.Errorf("CEL expression must return boolean, got: %T", out.Value()) | |
| } | |
| return result, nil | |
| } | |
| func main() { | |
| guard, err := NewCELGuard("data.amount == 100") | |
| if err != nil { | |
| fmt.Printf("Error creating guard: %v\n", err) | |
| return | |
| } | |
| token := &Token{ | |
| id: TokenID{value: "token-123"}, | |
| data: map[string]any{ | |
| "amount":100, | |
| }, | |
| } | |
| result := guard.Guard | |
| fmt.Printf("Guard result: %v\n", result) | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment