Skip to content

Instantly share code, notes, and snippets.

@K4L1Ma
Created December 23, 2025 11:28
Show Gist options
  • Select an option

  • Save K4L1Ma/32b3306237033538f8b2d467c59981ce to your computer and use it in GitHub Desktop.

Select an option

Save K4L1Ma/32b3306237033538f8b2d467c59981ce to your computer and use it in GitHub Desktop.
CEL Guard Implementation for Petri Net Tokens in Go
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