Releases: lustefaniak/cel-swift
Release list
cel-swift 0.1.1
cel-swift 0.1.1 is a performance release. Parsing and type checking now run at about cel-go's speed, and evaluation is up to twice as fast as in 0.1.0. The public API is the same as 0.1.0, and so is the conformance: cel-go v0.32.0 behaviour, tested against cel-spec v0.25.3.
Highlights
- Parsing takes 0.43–0.48× as long as in 0.1.0 (1.1–1.3× cel-go, down from 2.3–2.9×). Each parse-tree node used to get a weak-reference side table. The parser no longer does dynamic exclusivity checks. On Darwin, long inputs run on the calling thread when it has enough stack.
- Type checking takes 0.55–0.73× as long (0.8–1.1× cel-go). Types are looked up without being formatted into strings, expression ids are kept in a bitset, and a failed unification is undone instead of copying the type mapping. That copy made long expressions quadratic.
- Evaluation takes 0.51–0.88× as long (1.2–2× cel-go, down from 1.7–3.1×). Interpreter nodes share a native base class, so calls through them need no runtime type lookup. Maps and variable bindings with up to 8 entries are searched without hashing.
- Planning (
Environment.program) takes 0.90–0.97× as long, and 0.64–0.76× for trivial expressions. - Fix: a
CELSwiftexplanation of a condition that starts with a global function call, such assize(pr.labels) > 0, lost the function name.
Installation
.package(url: "https://github.com/lustefaniak/cel-swift.git", from: "0.1.1"),The products are unchanged: CEL, CELExtensions, CELProtobuf, CELPolicy, CELTest, CELSwift, and the cel-swift command-line tool.
Conformance
cel-spec v0.25.3, checked mode. The other implementations' numbers come from the skip lists their own conformance runners keep:
| Implementation | Passing |
|---|---|
| cel-swift 0.1.1 | 2508 / 2508 (100%) |
| cel-go v0.32.0 | 2380 / 2508 (94.9%) |
| cel-cpp | 2386 / 2508 (95.1%) |
| cel-rust | 1283 / 2508 (51.2%) |
Parse-only mode passes 2339 / 2339.
Platforms
These are the same as in 0.1.0:
- Swift 6.0 or newer, language mode 6, strict concurrency.
- macOS 13+, iOS 16+, and Linux with glibc or the static Linux SDK (musl).
- CI tests Swift 6.0 to 6.3 on Linux and the latest Xcode on macOS, and builds for the iOS simulator.
Performance
Against cel-go on the same expressions (docs/performance.md), each phase takes this many times as long as cel-go's:
| Phase | 0.1.1 | 0.1.0 |
|---|---|---|
| Parsing | 1.1 to 1.3× | 2 to 3× |
| Checking | 0.8 to 1.1× | 1.3 to 2× |
| Planning | 2.6 to 3× | about 3× |
| Evaluation | 1.2 to 2× | 1.6 to 3.3× |
Each change was measured against the main it merged into, running both builds in alternation (tools/bench/bench.py --baseline-driver) on an Apple M1 Max. The new tools in tools/bench (allocs.py, profile.py) count allocations and summarise xctrace profiles.
Breaking changes
None. swift package diagnose-api-breaking-changes 0.1.0 reports no breaking changes in CEL, CELExtensions, CELPolicy, CELTest or CELProtobuf.
Known limitations
These are unchanged from 0.1.0:
- The AST is not public yet, so there are no custom macros, optimizers or interpreter decorators.
- No dynamic protobuf messages from runtime descriptor sets.
- No proto conversion of checked expressions.
- No textproto test suites.
Full changelog: CHANGELOG.md
cel-swift 0.1.0
The first release of cel-swift, a pure-Swift implementation of the Common Expression Language, ported from cel-go v0.32.0 and tested against cel-spec v0.25.3.
Highlights
- 100% cel-spec conformance: all 2508 tests pass in checked mode and all 2339 in parse-only mode, with an empty skip list. That includes the strong-enum sections cel-go and cel-cpp skip.
- The whole of cel-go's runtime: parser, type checker, interpreter, cost estimation and limits, partial evaluation with unknowns and residual expressions, constant folding and inlining.
- cel-go's extension libraries: strings, lists, math, sets, encoders, network, regex, bindings, two-variable comprehensions and protos, with their versions and cost estimators.
- Policies and test suites: cel-go's YAML policy format, environment configs and
tests.yamlrunner, with the same results as cel-go'sceltest. - Swift types on both ends (
CELSwift):Codablefacts and results, CEL schemas derived from Swift types, typed Swift functions, and typed programs that check their outputs when they load and explain their results. - A zero-dependency core:
CELdepends on nothing. Protobuf and YAML live in their own products.
Installation
.package(url: "https://github.com/lustefaniak/cel-swift.git", from: "0.1.0"),| Product | Contents | Dependencies |
|---|---|---|
CEL |
parser, checker, interpreter, standard library, cost, partial evaluation, optimizers | none |
CELExtensions |
cel-go's extension libraries | CEL |
CELProtobuf |
protobuf messages, enums and well-known types as CEL values, generated by protoc-gen-cel-swift |
CEL, swift-protobuf |
CELPolicy |
cel-go's YAML policy format and environment configs | CEL, CELExtensions, Yams |
CELTest |
cel-go's tests.yaml suites |
CELPolicy |
CELSwift |
Codable facts and results, typed functions, typed programs and policies with explanations |
CEL, CELPolicy |
The package also builds the cel-swift command-line tool: eval, check, parse, repl and policy test.
import CEL
let env = try Environment(.variable("user", .map(key: .string, value: .dyn)))
let program = try env.program(env.compile("user.age >= 18 && user.country in ['PL', 'DK']"))
let result = try program.evaluate(["user": ["age": 30, "country": "PL"]])
print(result.value) // trueConformance
cel-spec v0.25.3, checked mode. The other implementations' numbers come from the skip lists their own conformance runners keep:
| Implementation | Passing |
|---|---|
| cel-swift 0.1.0 | 2508 / 2508 (100%) |
| cel-go v0.32.0 | 2380 / 2508 (94.9%) |
| cel-cpp | 2386 / 2508 (95.1%) |
| cel-rust | 1283 / 2508 (51.2%) |
Every test cel-go or cel-cpp passes, cel-swift passes too. Where cel-spec and cel-go disagree, cel-swift follows the spec and cel-cpp, and each difference is recorded in docs/divergences.md. A differential suite also compares values, errors, types and costs with cel-go on generated expressions; it runs nightly in CI.
Platforms
- Swift 6.0 or newer, language mode 6, strict concurrency.
- macOS 13+, iOS 16+, and Linux with glibc or the static Linux SDK (musl).
- CI tests Swift 6.0, 6.1, 6.2 and 6.3 on Linux and the latest Xcode on macOS, and builds for the iOS simulator.
- On Linux, IANA time zone names need the system tz database (
tzdata, plustzdata-legacyfor the oldUS/...names).
Performance
Against cel-go on the same expressions (docs/performance.md), each phase takes this many times as long as cel-go's:
| Phase | Time vs cel-go |
|---|---|
| Evaluation | 1.6 to 3.3× |
| Checking | 1.3 to 2× |
| Parsing | 2 to 3× |
| Planning | about 3× |
Concurrent parses on one Environment share a prediction cache and scale across threads.
Untrusted input
Expressions from untrusted sources always terminate, and estimateCost, the cost limit, a time limit and task cancellation bound the work. Policy YAML and environment configs are hardened against alias expansion, deep nesting and huge duplicate-key sets: these give errors instead of crashing or using memory out of proportion.
Known limitations
- The AST is not public yet, so there are no custom macros, optimizers or interpreter decorators.
- No dynamic protobuf messages from runtime descriptor sets.
- No proto conversion of checked expressions.
- No textproto test suites.
docs/decisions.md records the API decisions behind this release.
Full changelog: CHANGELOG.md