Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,5 @@
bin/
*.out
web/main.wasm
web/assets/main.wasm.gz
web/assets/data.json
17 changes: 11 additions & 6 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,19 @@
# Contributing to CEL Playground
# Contributing to Expr Playground

:+1::tada: Thanks for taking the time to contribute! :tada::+1:

The following is a set of guidelines for contributing to CEL Playground,
which are hosted in the [Undistro Organization](https://github.com/undistro) on GitHub.
The following is a set of guidelines for contributing to Expr Playground,
which is maintained by [polds](https://github.com/polds) on GitHub.

## CEL Playground

Expr playground is a fork of [CEL Playground](https://github.com/undistro/cel-playground) but swapping CEL support for
[Expr](https://expr-lang.org/) support. Please make sure to check out their project and give them a star as well!

## How Can I Contribute?

- **Giving us a star.** It may not seem like much, but it really makes a
difference. This is something that everyone can do to help out CEL Playground.
difference. This is something that everyone can do to help out Expr Playground.
GitHub stars help the project gain visibility and stand out.

- **Reviewing the documentation.**
Expand All @@ -17,9 +22,9 @@ which are hosted in the [Undistro Organization](https://github.com/undistro) on
feel free to open a Pull Request with your suggestions.

- **Reporting bugs.** We use GitHub issues to track public bugs.
Report a bug by [opening a new issue](https://github.com/undistro/cel-playground/issues/new/choose).
Report a bug by [opening a new issue](https://github.com/polds/expr-playground/issues/new/choose).

- **Pull Request.** Unless you are fixing a known bug, we **strongly**
recommend discussing it with the core team via a GitHub issue before
getting started to ensure your work is consistent with CEL Playground's roadmap and architecture.
getting started to ensure your work is consistent with Expr Playground's roadmap and architecture.

16 changes: 7 additions & 9 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
CEL_GO_VERSION=$(shell go list -f "{{.Version}}" -m github.com/google/cel-go)
EXPR_GO_VERSION=$(shell go list -f "{{.Version}}" -m github.com/expr-lang/expr)

# Setting SHELL to bash allows bash commands to be executed by recipes.
# Options are set to exit when a recipe line exits non-zero or a piped command fails.
Expand All @@ -25,31 +25,29 @@ test: fmt ## Run tests.
go test ./... -coverprofile cover.out

.PHONY: serve
serve: ## Serve static files.
python3 -m http.server -d web/ 8080
#Uncomment the command below to serve with Go
#go run cmd/server/main.go --dir web/
serve: build ## Serve static files.
go run cmd/server/main.go

.PHONY: update-data
update-data: ## Update the web/assets/data.json file.
yq -ojson '.' examples.yaml > web/assets/data.json
yq -ojson -i '.versions.cel-go = "$(CEL_GO_VERSION)"' web/assets/data.json
yq -ojson -i '.versions.expr = "$(EXPR_GO_VERSION)"' web/assets/data.json

.PHONY: addlicense
addlicense: ## Add copyright license headers in source code files.
@test -s $(LOCALBIN)/addlicense || GOBIN=$(LOCALBIN) go install github.com/google/addlicense@latest
$(LOCALBIN)/addlicense -c "Undistro Authors" -l "apache" -ignore ".github/**" -ignore ".idea/**" -ignore "web/dist/**" .
$(LOCALBIN)/addlicense -c "Undistro Authors; Fork and conversion to Expr by Peter Olds <me@polds.dev>" -l "apache" -ignore ".github/**" -ignore ".idea/**" -ignore "web/dist/**" .

.PHONY: checklicense
checklicense: ## Check copyright license headers in source code files.
@test -s $(LOCALBIN)/addlicense || GOBIN=$(LOCALBIN) go install github.com/google/addlicense@latest
$(LOCALBIN)/addlicense -c "Undistro Authors" -l "apache" -ignore ".github/**" -ignore ".idea/**" -ignore "web/dist/**" -check .
$(LOCALBIN)/addlicense -c "Undistro Authors; Fork and conversion to Expr by Peter Olds <me@polds.dev>" -l "apache" -ignore ".github/**" -ignore ".idea/**" -ignore "web/dist/**" -check .

##@ Build

.PHONY: build
build: fmt update-data ## Build the wasm binary.
GOOS=js GOARCH=wasm go build -ldflags="-s -w" -o web/assets/main.wasm cmd/wasm/main.go
GOOS=js GOARCH=wasm go build -trimpath -ldflags="-s -w" -o web/assets/main.wasm cmd/wasm/main.go
gzip --best -f web/assets/main.wasm

## Location to install dependencies to
Expand Down
31 changes: 17 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,20 +1,23 @@
# CEL Playground
![GitHub](https://img.shields.io/github/license/undistro/cel-playground)
[![Go Report Card](https://goreportcard.com/badge/github.com/undistro/cel-playground)](https://goreportcard.com/report/github.com/undistro/cel-playground)
# Expr Playground
![GitHub](https://img.shields.io/github/license/polds/expr-playground)
[![Go Report Card](https://goreportcard.com/badge/github.com/polds/expr-playground)](https://goreportcard.com/report/github.com/polds/expr-playground)

CEL Playground is an interactive WebAssembly (Wasm) powered environment to explore and experiment with the [Common Expression Language (CEL)](https://github.com/google/cel-spec).
It provides a simple and user-friendly interface to write and quickly evaluate CEL expressions.
Expr Playground is an interactive WebAssembly (Wasm) powered environment to explore and experiment with
[Expr-lang](https://expr-lang.org/). It provides a simple and user-friendly interface to write and quickly evaluate
Expr expressions.

## CEL libraries
## Credits

CEL Playground is built by compiling Go code to WebAssembly and includes the following libraries that are available in the environment:
This project is forked from [CEL Playground](https://github.com/undistro/cel-playground) and modified to support Expr.
Please be sure to check out their project and give them a star as well!

- CEL [extended string function library](https://pkg.go.dev/github.com/google/cel-go/ext#Strings)
- [Kubernetes list library](https://kubernetes.io/docs/reference/using-api/cel/#kubernetes-list-library)
- [Kubernetes regex library](https://kubernetes.io/docs/reference/using-api/cel/#kubernetes-regex-library)
- [Kubernetes URL library](https://kubernetes.io/docs/reference/using-api/cel/#kubernetes-url-library)
## Expr libraries

Take a look at [all the environment options](eval/eval.go#L26).
CEL Playground is built by compiling Go code to WebAssembly. At present only the Expr engine is available in this
environment. We will look at injecting some other utilities to make this environment more useful, on-par with the the
CEL standard library and CEL Playground.

Take a look at [all the environment options](eval/eval.go#L31).

## Development

Expand All @@ -31,8 +34,8 @@ make serve
## Contributing

We appreciate your contribution.
Please refer to our [contributing guideline](https://github.com/undistro/cel-playground/blob/main/CONTRIBUTING.md) for further information.
This project adheres to the Contributor Covenant [code of conduct](https://github.com/undistro/cel-playground/blob/main/CODE_OF_CONDUCT.md).
Please refer to our [contributing guideline](https://github.com/polds/expr-playground/blob/main/CONTRIBUTING.md) for further information.
This project adheres to the Contributor Covenant [code of conduct](https://github.com/polds/expr-playground/blob/main/CODE_OF_CONDUCT.md).

## License

Expand Down
6 changes: 4 additions & 2 deletions cmd/server/main.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
// Copyright 2023 Undistro Authors
// Modifications Fork and conversion to Expr Copyright 2024 Peter Olds <me@polds.dev>
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
Expand All @@ -18,16 +19,17 @@ import (
"flag"
"log"
"net/http"

"github.com/polds/expr-playground/web"
)

var (
listen = flag.String("listen", ":8080", "listen address")
dir = flag.String("dir", ".", "directory to serve")
)

func main() {
flag.Parse()
log.Printf("listening on %s...", *listen)
err := http.ListenAndServe(*listen, http.FileServer(http.Dir(*dir)))
err := http.ListenAndServe(*listen, http.FileServer(http.FS(web.Assets)))
log.Fatalln(err)
}
3 changes: 2 additions & 1 deletion cmd/wasm/main.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
// Copyright 2023 Undistro Authors
// Modifications Fork and conversion to Expr Copyright 2024 Peter Olds <me@polds.dev>
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
Expand All @@ -23,7 +24,7 @@ import (

"gopkg.in/yaml.v3"

"github.com/undistro/cel-playground/eval"
"github.com/polds/expr-playground/eval"
)

func main() {
Expand Down
110 changes: 17 additions & 93 deletions eval/eval.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
// Copyright 2023 Undistro Authors
// Modifications Fork and conversion to Expr Copyright 2024 Peter Olds <me@polds.dev>
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
Expand All @@ -17,116 +18,39 @@ package eval
import (
"encoding/json"
"fmt"
"reflect"

"github.com/google/cel-go/cel"
"github.com/google/cel-go/checker"
"github.com/google/cel-go/common/types/ref"
"github.com/google/cel-go/ext"
"github.com/google/cel-go/interpreter"
"google.golang.org/protobuf/types/known/structpb"
k8s "k8s.io/apiserver/pkg/cel/library"
"github.com/expr-lang/expr"
"github.com/expr-lang/expr/vm"
)

type EvalResponse struct {
Result any `json:"result"`
Cost *uint64 `json:"cost, omitempty"`
type RunResponse struct {
Result any `json:"result"`
Bytecode []vm.Opcode `json:"bytecode"`
}

var celEnvOptions = []cel.EnvOption{
// 1.0 (1.23)
cel.HomogeneousAggregateLiterals(),
cel.EagerlyValidateDeclarations(true),
cel.DefaultUTCTimeZone(true),
k8s.URLs(),
k8s.Regex(),
k8s.Lists(),

// 1.27
// k8s.Authz(),

// 1.28
cel.CrossTypeNumericComparisons(true),
cel.OptionalTypes(),
k8s.Quantity(),

// 1.29 (see also validator.ExtendedValidations())
cel.ASTValidators(
cel.ValidateDurationLiterals(),
cel.ValidateTimestampLiterals(),
cel.ValidateRegexLiterals(),
cel.ValidateHomogeneousAggregateLiterals(),
),

// Strings (from 1.29 onwards)
ext.Strings(ext.StringsVersion(2)),
// Set library (1.29 onwards)
ext.Sets(),

// cel-go v0.17.7 introduced CostEstimatorOptions.
// Previous the presence has a cost of 0 but cel fixed it to 1. We still set to 0 here to avoid breaking changes.
cel.CostEstimatorOptions(checker.PresenceTestHasCost(false)),
var exprEnvOptions = []expr.Option{
expr.AsAny(),
}

var celProgramOptions = []cel.ProgramOption{
cel.EvalOptions(cel.OptOptimize, cel.OptTrackCost),

// cel-go v0.17.7 introduced CostTrackerOptions.
// Previous the presence has a cost of 0 but cel fixed it to 1. We still set to 0 here to avoid breaking changes.
cel.CostTrackerOptions(interpreter.PresenceTestHasCost(false)),
}

// Eval evaluates the cel expression against the given input
// Eval evaluates the expr expression against the given input.
func Eval(exp string, input map[string]any) (string, error) {
inputVars := make([]cel.EnvOption, 0, len(input))
for k := range input {
inputVars = append(inputVars, cel.Variable(k, cel.DynType))
}
env, err := cel.NewEnv(append(celEnvOptions, inputVars...)...)
if err != nil {
return "", fmt.Errorf("failed to create CEL env: %w", err)
}
ast, issues := env.Compile(exp)
if issues != nil {
return "", fmt.Errorf("failed to compile the CEL expression: %s", issues.String())
}
prog, err := env.Program(ast, celProgramOptions...)
exprEnvOptions = append(exprEnvOptions, expr.Env(input))
program, err := expr.Compile(exp, exprEnvOptions...)
if err != nil {
return "", fmt.Errorf("failed to instantiate CEL program: %w", err)
return "", fmt.Errorf("failed to compile the Expr expression: %w", err)
}
val, costTracker, err := prog.Eval(input)
output, err := expr.Run(program, input)
if err != nil {
return "", fmt.Errorf("failed to evaluate: %w", err)
}

response, err := generateResponse(val, costTracker)
if err != nil {
return "", fmt.Errorf("failed to generate the response: %w", err)
res := &RunResponse{
Result: output,
Bytecode: program.Bytecode,
}

out, err := json.Marshal(response)
out, err := json.MarshalIndent(res, "", " ")
if err != nil {
return "", fmt.Errorf("failed to marshal the output: %w", err)
}
return string(out), nil
}

func getResults(val *ref.Val) (any, error) {
if value, err := (*val).ConvertToNative(reflect.TypeOf(&structpb.Value{})); err != nil {
return nil, err
} else {
return value, nil
}
}

func generateResponse(val ref.Val, costTracker *cel.EvalDetails) (*EvalResponse, error) {
result, evalError := getResults(&val)
if evalError != nil {
return nil, evalError
}
cost := costTracker.ActualCost()
return &EvalResponse{
Result: result,
Cost: cost,
}, nil
}
Loading