This document serves as the unified API Doc reference for the Common Expression Language (CEL). It lists all macros, operators, and standard functions, indicating their signatures, behaviors, and support status across the official CEL stacks.
For more details on language behavior and specifications, refer to the CEL Language Definition.
Stack Versions
This reference document is based on the following versions of the CEL stacks:
- CEL Go:
v0.29.2(and newer) - CEL C++:
v0.15.0 - CEL Java:
v0.13.1 - CEL Python:
v0.1.3 - CEL C: Development snapshot (unreleased)
GitHub Mirrors
The official implementations of CEL are mirrored on GitHub under the cel-expr
organization:
1. Core Macros
These are built-in macros that are expanded at compile time.
| Macro | Description | Go | C++ | Java | Python | C |
|---|---|---|---|---|---|---|
has(container.field) |
Tests whether a field is present in a message, or a key in a map. Signatures: has(container.field) -> boolExamples: has(request.auth.claims.email) |
✓ | ✓ | ✓ | ✓ | ✓ |
list.all(var, predicate) |
Tests whether all elements in a list satisfy a predicate. Signatures: list.all(var, predicate) -> boolExamples: [1, 2, 3].all(x, x > 0) // true |
✓ | ✓ | ✓ | ✓ | ✓¹ |
list.exists(var, predicate) |
Tests whether at least one element in a list satisfies a predicate. Signatures: list.exists(var, predicate) -> boolExamples: [1, 2, 3].exists(x, x > 2) // true |
✓ | ✓ | ✓ | ✓ | ✓¹ |
list.exists_one(var, predicate) |
Tests whether exactly one element in a list satisfies a predicate. Signatures: list.exists_one(var, predicate) -> boolExamples: [1, 2, 3].exists_one(x, x == 2) // true |
✓ | ✓ | ✓ | ✓ | ✓¹ |
list.filter(var, predicate) |
Filters elements of a list according to a predicate. Signatures: list.filter(var, predicate) -> listExamples: [1, 2, 3].filter(x, x > 1) // [2, 3] |
✓ | ✓ | ✓ | ✓ | ✓¹ |
list.map(var, transform) |
Transforms each element of a list using an expression. Signatures: list.map(var, transform) -> listExamples: [1, 2, 3].map(x, x * 2) // [2, 4, 6] |
✓ | ✓ | ✓ | ✓ | ✓¹ |
list.map(var, filter, transform) |
Transforms elements of a list that satisfy a filter predicate. Signatures: list.map(var, filter, transform) -> listExamples: [1, 2, 3].map(x, x > 1, x * 2) // [4, 6] |
✓ | ✓ | ✓ | ✓ | ✓¹ |
¹ Supported in C runtime because macros are expanded into comprehensions during compilation by the host compiler.
2. Core Operators
| Operator | Description | Go | C++ | Java | Python | C |
|---|---|---|---|---|---|---|
Arithmetic (+, -, *, /, %) |
Standard arithmetic operations. Negation (-x) and Identity (+x). List concatenation (list + list) is supported in Go, C++, Java, and Python.Signatures: T + T -> TT - T -> TT * T -> TT / T -> TT % T -> T-T -> T+T -> Tlist + list -> listExamples: 1 + 2 * 3 // 7[1] + [2] // [1, 2] |
✓ | ✓ | ✓ | ✓ | ✓² |
Comparison (==, !=, <, <=, >, >=) |
Standard comparison. Numeric comparisons are heterogeneous (e.g. 1 == 1.0).Signatures: T == T -> boolT != T -> boolT < T -> boolT <= T -> boolT > T -> boolT >= T -> boolExamples: x < 42.01 == 1.0 // true |
✓ | ✓ | ✓ | ✓ | ✓ |
Logical (!, &&, ||, ? :) |
Logical NOT, AND, OR, and Ternary Conditional. AND/OR use short-circuit evaluation. Signatures: !bool -> boolbool && bool -> boolbool || bool -> boolbool ? T : T -> TExamples: x > 0 ? "positive" : "non-positive" |
✓ | ✓ | ✓ | ✓ | ✓ |
Indexing ([]) |
Access element of a list by index, or lookup key in a map. Signatures: list[int] -> Tmap[K] -> VExamples: tags[0]users['john'] |
✓ | ✓ | ✓ | ✓ | ✓ |
Membership (in) |
Check if element is in a list, or key is in a map. Signatures: T in list -> boolK in map -> boolExamples: 'admin' in roles |
✓ | ✓ | ✓ | ✓ | ✓ |
² List concatenation (list + list) is not supported in the C runtime,
though other arithmetic operators are supported.
3. Core Functions
General & String Functions
| Function | Description | Go | C++ | Java | Python | C |
|---|---|---|---|---|---|---|
size |
Returns the size of a string (characters), bytes, list, or map. Signatures: size(T) -> int (where T is string, bytes, list, or map)<br /><br />**Examples:**<br />size("hello") // 5` |
✓ | ✓ | ✓ | ✓ | ✓ |
contains |
Returns whether string contains substring. Signatures: string.contains(string) -> boolExamples: "hello".contains("ell") // true |
✓ | ✓ | ✓ | ✓ | ✓ |
startsWith |
Returns whether string starts with prefix. Signatures: string.startsWith(string) -> boolExamples: "hello".startsWith("he") // true |
✓ | ✓ | ✓ | ✓ | ✓ |
endsWith |
Returns whether string ends with suffix. Signatures: string.endsWith(string) -> boolExamples: "hello".endsWith("lo") // true |
✓ | ✓ | ✓ | ✓ | ✓ |
matches |
Returns whether string matches RE2 regular expression. Signatures: string.matches(string) -> boolExamples: "123".matches(r"^\d+$") // true |
✓ | ✓ | ✓ | ✓ | ✓ |
Date and Time Selector Functions
These functions extract components from google.protobuf.Timestamp or
google.protobuf.Duration.
| Function | Description | Go | C++ | Java | Python | C |
|---|---|---|---|---|---|---|
getFullYear |
Returns the 4-digit year. Signatures: timestamp.getFullYear([tz]) -> intExamples: timestamp("2026-07-23T00:00:00Z").getFullYear() // 2026 |
✓ | ✓ | ✓ | ✓ | ✗ |
getMonth |
Returns the month (0-11). Signatures: timestamp.getMonth([tz]) -> intExamples: timestamp("2026-07-23T00:00:00Z").getMonth() // 6 |
✓ | ✓ | ✓ | ✓ | ✗ |
getDayOfMonth |
Returns the day of the month (1-31). Signatures: timestamp.getDayOfMonth([tz]) -> intExamples: timestamp("2026-07-23T00:00:00Z").getDayOfMonth() // 23 |
✓ | ✓ | ✓ | ✓ | ✗ |
getDayOfWeek |
Returns the day of the week (0 = Sunday). Signatures: timestamp.getDayOfWeek([tz]) -> intExamples: timestamp("2026-07-23T00:00:00Z").getDayOfWeek() // 4 |
✓ | ✓ | ✓ | ✓ | ✗ |
getDayOfYear |
Returns the day of the year (0-365). Signatures: timestamp.getDayOfYear([tz]) -> intExamples: timestamp("2026-07-23T00:00:00Z").getDayOfYear() // 203 |
✓ | ✓ | ✓ | ✓ | ✗ |
getHours |
Returns the hours (0-23). Signatures: timestamp.getHours([tz]) -> intduration.getHours() -> intExamples: duration("1h30m").getHours() // 1 |
✓ | ✓ | ✓ | ✓ | ✗ |
getMinutes |
Returns the minutes (0-59). Signatures: timestamp.getMinutes([tz]) -> intduration.getMinutes() -> intExamples: duration("1h30m").getMinutes() // 30 |
✓ | ✓ | ✓ | ✓ | ✗ |
getSeconds |
Returns the seconds (0-59). Signatures: timestamp.getSeconds([tz]) -> intduration.getSeconds() -> intExamples: duration("1h30m45s").getSeconds() // 45 |
✓ | ✓ | ✓ | ✓ | ✗ |
getMilliseconds |
Returns the milliseconds (0-999). Signatures: timestamp.getMilliseconds([tz]) -> intduration.getMilliseconds() -> intExamples: duration("1.5s").getMilliseconds() // 500 |
✓ | ✓ | ✓ | ✓ | ✗ |
Type Conversions
| Target Type | Description | Go | C++ | Java | Python | C |
|---|---|---|---|---|---|---|
bool |
Converts to boolean. Signatures: bool(bool) -> boolbool(string) -> boolExamples: bool("true") // true |
✓ | ✓ | ✓ | ✓ | ✓ |
bytes |
Converts to bytes. Signatures: bytes(bytes) -> bytesbytes(string) -> bytesExamples: bytes("hello") // b"hello" |
✓ | ✓ | ✓ | ✓ | ✓ |
double |
Converts to double-precision float. Signatures: double(double) -> doubledouble(int) -> doubledouble(uint) -> doubledouble(string) -> doubleExamples: double(1) // 1.0 |
✓ | ✓ | ✓ | ✓ | ✓ |
duration |
Converts to duration. Signatures: duration(duration) -> durationduration(string) -> durationExamples: duration("1.5s") // 1.5s duration |
✓ | ✓ | ✓ | ✓ | ✓ |
int |
Converts to 64-bit signed integer. Signatures: int(int) -> intint(uint) -> intint(double) -> int (rounds to zero)int(string) -> intint(timestamp) -> int (seconds since epoch)Examples: int(1.5) // 1 |
✓ | ✓ | ✓ | ✓ | ✓ |
string |
Converts to string. Signatures: string(T) -> string (supports bool, int, uint, double, bytes, timestamp, duration)<br /><br />**Examples:**<br />string(1.5) // "1.5"` |
✓ | ✓ | ✓ | ✓ | ✓ |
timestamp |
Converts to timestamp. Signatures: timestamp(timestamp) -> timestamptimestamp(string) -> timestamp (RFC3339)Examples: timestamp("2026-07-23T00:00:00Z") |
✓ | ✓ | ✓ | ✓ | ✓ |
uint |
Converts to 64-bit unsigned integer. Signatures: uint(uint) -> uintuint(int) -> uintuint(double) -> uintuint(string) -> uintExamples: uint(1) // 1u |
✓ | ✓ | ✓ | ✓ | ✓ |
dyn |
Casts value to dynamic type for type-checking. Signatures: dyn(T) -> dynExamples: dyn([1, "two"]) |
✓ | ✓ | ✓ | ✓ | ✗ |
type |
Returns the type of the value. Signatures: type(T) -> typeExamples: type(1) // int |
✓ | ✓ | ✓ | ✓ | ✗ |
4. Extensions (Libraries)
Bindings Library
| Function | Description | Go | C++ | Java | Python | C |
|---|---|---|---|---|---|---|
cel.bind |
Binds a local variable to avoid duplicate evaluation. Signatures: cel.bind(varName, initExpr, resultExpr) -> TExamples: cel.bind(x, a + b, x * x) |
✓ (v0.15.0) | ✓ (v0.10.0) | ✓ (v0.2.0) | ✓ (v0.1.1) | ✗ |
How to Enable
- Go: Pass
ext.Bindings()tocel.NewEnv(). - C++: Add
BindingsCompilerLibrary()toCompilerBuilder. (Runtime is handled automatically). - Java: Add
CelExtensions.bindings()toCelCompilerandCelRuntimebuilders. - Python: Import
cel_expr_python.ext.ext_bindingsand useExtBindings()incel.NewEnv(extensions=[...]).
Encoders Library
| Function | Description | Go | C++ | Java | Python | C |
|---|---|---|---|---|---|---|
base64.encode |
Encodes bytes to base64 string. Signatures: base64.encode(bytes) -> stringExamples: base64.encode(b"hello") // "aGVsbG8=" |
✓ (v0.6.0) | ✓ (v0.10.0) | ✓ (v0.2.0) | ✓ (v0.1.1) | ✗ |
base64.decode |
Decodes base64 string to bytes. Throws error on invalid input. Signatures: base64.decode(string) -> bytesExamples: base64.decode("aGVsbG8=") // b"hello" |
✓ (v0.6.0) | ✓ (v0.10.0) | ✓ (v0.2.0) | ✓ (v0.1.1) | ✗ |
json.encode |
Serializes a CEL value to JSON string. Signatures: json.encode(dyn) -> stringExamples: json.encode([1, 2]) // "[1,2]" |
✓ (v0.29.0) | ✗ | ✗ | ✗ | ✗ |
How to Enable
- Go: Pass
ext.Encoders()tocel.NewEnv(). - C++:
- Compiler: Add
EncodersCompilerLibrary()toCompilerBuilder. - Runtime: Call
RegisterEncodersFunctions()onFunctionRegistry.
- Compiler: Add
- Java: Add
CelExtensions.encoders()toCelCompilerandCelRuntimebuilders. - Python: Import
cel_expr_python.ext.ext_encodersand useExtEncoders()incel.NewEnv(extensions=[...]).
Math Library
| Function | Description | Go | C++ | Java | Python | C |
|---|---|---|---|---|---|---|
math.greatest |
Returns greatest of numeric arguments (or list of numerics). Signatures: math.greatest(arg, ...) -> TExamples: math.greatest(1, 3, 2) // 3 |
✓ (v0.13.0) | ✓ (v0.10.0) | ✓ (v0.2.0) | ✓ (v0.1.1) | ✗ |
math.least |
Returns least of numeric arguments (or list of numerics). Signatures: math.least(arg, ...) -> TExamples: math.least([1, 3, 2]) // 1 |
✓ (v0.13.0) | ✓ (v0.10.0) | ✓ (v0.2.0) | ✓ (v0.1.1) | ✗ |
math.abs |
Absolute value. Signatures: math.abs(T) -> T (supports int, uint, double)<br /><br />**Examples:**<br />math.abs(-1) // 1` |
✓ (v0.21.0) | ✓ (v0.11.0) | ✓ (v0.10.1) | ✓ (v0.1.1) | ✗ |
math.sqrt |
Square root. Signatures: math.sqrt(T) -> double (supports int, uint, double)<br /><br />**Examples:**<br />math.sqrt(9) // 3.0` |
✓ (v0.25.1) | ✓ (v0.12.0) | ✓ (v0.11.0) | ✓ (v0.1.1) | ✗ |
math.bitAnd |
Bitwise AND. Signatures: math.bitAnd(T, T) -> T (supports int, uint)<br /><br />**Examples:**<br />math.bitAnd(5, 3) // 1` |
✓ (v0.21.0) | ✓ (v0.11.0) | ✓ (v0.10.1) | ✓ (v0.1.1) | ✗ |
math.bitOr |
Bitwise OR. Signatures: math.bitOr(T, T) -> T (supports int, uint)<br /><br />**Examples:**<br />math.bitOr(5, 3) // 7` |
✓ (v0.21.0) | ✓ (v0.11.0) | ✓ (v0.10.1) | ✓ (v0.1.1) | ✗ |
math.bitXor |
Bitwise XOR. Signatures: math.bitXor(T, T) -> T (supports int, uint)<br /><br />**Examples:**<br />math.bitXor(5, 3) // 6` |
✓ (v0.21.0) | ✓ (v0.11.0) | ✓ (v0.10.1) | ✓ (v0.1.1) | ✗ |
math.bitNot |
Bitwise NOT. Signatures: math.bitNot(T) -> T (supports int, uint)<br /><br />**Examples:**<br />math.bitNot(1) // -2` |
✓ (v0.21.0) | ✓ (v0.11.0) | ✓ (v0.10.1) | ✓ (v0.1.1) | ✗ |
math.bitShiftLeft |
Bitwise shift left. Signatures: math.bitShiftLeft(T, int) -> T (supports int, uint)<br /><br />**Examples:**<br />math.bitShiftLeft(1, 2) // 4` |
✓ (v0.21.0) | ✓ (v0.11.0) | ✓ (v0.10.1) | ✓ (v0.1.1) | ✗ |
math.bitShiftRight |
Bitwise shift right. Signatures: math.bitShiftRight(T, int) -> T (supports int, uint)<br /><br />**Examples:**<br />math.bitShiftRight(4, 2) // 1` |
✓ (v0.21.0) | ✓ (v0.11.0) | ✓ (v0.10.1) | ✓ (v0.1.1) | ✗ |
math.ceil |
Ceiling rounding. Signatures: math.ceil(double) -> doubleExamples: math.ceil(1.2) // 2.0 |
✓ (v0.21.0) | ✓ (v0.11.0) | ✓ (v0.10.1) | ✓ (v0.1.1) | ✗ |
math.floor |
Floor rounding. Signatures: math.floor(double) -> doubleExamples: math.floor(1.8) // 1.0 |
✓ (v0.21.0) | ✓ (v0.11.0) | ✓ (v0.10.1) | ✓ (v0.1.1) | ✗ |
math.round |
Nearest integer rounding. Signatures: math.round(double) -> doubleExamples: math.round(1.5) // 2.0 |
✓ (v0.21.0) | ✓ (v0.11.0) | ✓ (v0.10.1) | ✓ (v0.1.1) | ✗ |
math.trunc |
Truncation rounding (towards zero). Signatures: math.trunc(double) -> doubleExamples: math.trunc(-1.8) // -1.0 |
✓ (v0.21.0) | ✓ (v0.11.0) | ✓ (v0.10.1) | ✓ (v0.1.1) | ✗ |
math.isInf |
Checks if double is positive or negative infinity. Signatures: math.isInf(double) -> boolExamples: math.isInf(1.0/0.0) // true |
✓ (v0.21.0) | ✓ (v0.11.0) | ✓ (v0.10.1) | ✓ (v0.1.1) | ✗ |
math.isNaN |
Checks if double is NaN. Signatures: math.isNaN(double) -> boolExamples: math.isNaN(0.0/0.0) // true |
✓ (v0.21.0) | ✓ (v0.11.0) | ✓ (v0.10.1) | ✓ (v0.1.1) | ✗ |
math.isFinite |
Checks if double is finite. Signatures: math.isFinite(double) -> boolExamples: math.isFinite(1.2) // true |
✓ (v0.21.0) | ✓ (v0.11.0) | ✓ (v0.10.1) | ✓ (v0.1.1) | ✗ |
math.sign |
Returns sign of value (-1, 0, or 1). Signatures: math.sign(T) -> T (supports int, uint, double)<br /><br />**Examples:**<br />math.sign(-42) // -1` |
✓ (v0.21.0) | ✓ (v0.11.0) | ✓ (v0.10.1) | ✓ (v0.1.1) | ✗ |
How to Enable
- Go: Pass
ext.Math()tocel.NewEnv(). - C++:
- Compiler: Add
MathCompilerLibrary()toCompilerBuilder. - Runtime: Call
RegisterMathExtensionFunctions()onFunctionRegistry.
- Compiler: Add
- Java: Add
CelExtensions.math()toCelCompilerandCelRuntimebuilders. - Python: Import
cel_expr_python.ext.ext_mathand useExtMath()incel.NewEnv(extensions=[...]).
Protos Library
| Function | Description | Go | C++ | Java | Python | C |
|---|---|---|---|---|---|---|
proto.getExt |
Gets proto2 extension field, or default if unset. Signatures: proto.getExt(msg, extName) -> TExamples: proto.getExt(msg, google.api.expr.test.int32_ext) |
✓ (v0.13.0) | ✓ (v0.10.0) | ✓ (v0.2.0) | ✓ (v0.1.1) | ✗ |
proto.hasExt |
Checks if proto2 extension field is set. Signatures: proto.hasExt(msg, extName) -> boolExamples: proto.hasExt(msg, google.api.expr.test.int32_ext) |
✓ (v0.13.0) | ✓ (v0.10.0) | ✓ (v0.2.0) | ✓ (v0.1.1) | ✗ |
How to Enable
- Go: Pass
ext.Protos()tocel.NewEnv(). - C++: Add
ProtoExtCompilerLibrary()toCompilerBuilder. (Runtime is handled automatically). - Java: Add
CelExtensions.protos()toCelCompilerandCelRuntimebuilders. - Python: Import
cel_expr_python.ext.ext_protoand useExtProto()incel.NewEnv(extensions=[...]).
Lists Library
| Function | Description | Go | C++ | Java | Python | C |
|---|---|---|---|---|---|---|
distinct |
Returns distinct elements. Signatures: list.distinct() -> listExamples: [1, 2, 2].distinct() // [1, 2] |
✓ (v0.22.0) | ✓ (v0.11.0) | ✓ (v0.11.0) | ✓ (v0.1.1) | ✗ |
flatten |
Flattens nested lists. Signatures: list.flatten([depth]) -> listExamples: [[1], [2, 3]].flatten() // [1, 2, 3] |
✓ (v0.22.0) | ✓ (v0.11.0) | ✓ (v0.7.1) | ✓ (v0.1.1) | ✗ |
lists.range |
Returns list of integers [0, ..., n-1].Signatures: lists.range(int) -> list(int)Examples: lists.range(3) // [0, 1, 2] |
✓ (v0.22.0) | ✓ (v0.11.0) | ✓ (v0.10.1) | ✓ (v0.1.1) | ✗ |
reverse |
Reverses the list. Signatures: list.reverse() -> listExamples: [1, 2].reverse() // [2, 1] |
✓ (v0.22.0) | ✓ (v0.11.0) | ✓ (v0.11.0) | ✓ (v0.1.1) | ✗ |
slice |
Returns sub-list (start inclusive, end exclusive). Signatures: list.slice(start, end) -> listExamples: [1, 2, 3].slice(1, 3) // [2, 3] |
✓ (v0.17.0) | ✓ (v0.11.0) | ✓ (v0.11.0) | ✓ (v0.1.1) | ✗ |
sort |
Sorts list of comparable elements. Signatures: list.sort() -> listExamples: [3, 1, 2].sort() // [1, 2, 3] |
✓ (v0.22.0) | ✓ (v0.11.0) | ✓ (v0.11.0) | ✓ (v0.1.1) | ✗ |
sortBy |
Sorts list by key evaluated from expression. Signatures: list.sortBy(var, expr) -> listExamples: [{"val": 2}, {"val": 1}].sortBy(x, x.val) // [{"val": 1}, {"val": 2}] |
✓ (v0.22.0) | ✓ (v0.11.0) | ✓ (v0.11.0) | ✓ (v0.1.1) | ✗ |
first |
Returns first element as optional. Requires the Optional extension. Signatures: list.first() -> optionalExamples: [1, 2].first() // optional(1) |
✓ (v0.23.0) | ✓ (v0.15.0) | ✓ (v0.11.0) | ✓ (v0.1.2) | ✗ |
last |
Returns last element as optional. Requires the Optional extension. Signatures: list.last() -> optionalExamples: [1, 2].last() // optional(2) |
✓ (v0.23.0) | ✓ (v0.15.0) | ✓ (v0.11.0) | ✓ (v0.1.2) | ✗ |
How to Enable
- Go: Pass
ext.Lists()tocel.NewEnv(). - C++:
- Compiler: Add
ListsCompilerLibrary()toCompilerBuilder. - Runtime: Call
RegisterListsFunctions()onFunctionRegistryandRegisterListsMacros()onMacroRegistry.
- Compiler: Add
- Java: Add
CelExtensions.lists()toCelCompilerandCelRuntimebuilders. - Python: Enable via
cel.EnvConfigby addingliststo theextensionslist.
Sets Library
| Function | Description | Go | C++ | Java | Python | C |
|---|---|---|---|---|---|---|
sets.contains |
Checks if list1 contains all elements of list2. Signatures: sets.contains(list1, list2) -> boolExamples: sets.contains([1, 2], [1]) // true |
✓ (v0.15.0) | ✓ (v0.10.0) | ✓ (v0.6.0) | ✓ (v0.1.1) | ✗ |
sets.equivalent |
Checks if lists are set-equivalent (contain same unique elements). Signatures: sets.equivalent(list1, list2) -> boolExamples: sets.equivalent([1, 2], [2, 1, 1]) // true |
✓ (v0.15.0) | ✓ (v0.10.0) | ✓ (v0.6.0) | ✓ (v0.1.1) | ✗ |
sets.intersects |
Checks if lists share at least one element. Signatures: sets.intersects(list1, list2) -> boolExamples: sets.intersects([1, 2], [2, 3]) // true |
✓ (v0.15.0) | ✓ (v0.10.0) | ✓ (v0.6.0) | ✓ (v0.1.1) | ✗ |
How to Enable
- Go: Pass
ext.Sets()tocel.NewEnv(). - C++:
- Compiler: Add
SetsCompilerLibrary()toCompilerBuilder. - Runtime: Call
RegisterSetsFunctions()onFunctionRegistry.
- Compiler: Add
- Java: Add
CelExtensions.sets()toCelCompilerandCelRuntimebuilders. - Python: Enable via
cel.EnvConfigby addingsetsto theextensionslist.
Strings Library
| Function | Description | Go | C++ | Java | Python | C |
|---|---|---|---|---|---|---|
charAt |
Returns character at index. Signatures: string.charAt(int) -> stringExamples: "hello".charAt(1) // "e" |
✓ (v0.4.0) | ✓ (v0.10.0) | ✓ (v0.2.0) | ✓ (v0.1.1) | ✗ |
indexOf |
Returns index of first occurrence of substring, or -1. Signatures: string.indexOf(substr, [start]) -> intExamples: "hello".indexOf("l") // 2 |
✓ (v0.4.0) | ✓ (v0.10.0) | ✓ (v0.2.0) | ✓ (v0.1.1) | ✗ |
lastIndexOf |
Returns index of last occurrence of substring, or -1. Signatures: string.lastIndexOf(substr, [end]) -> intExamples: "hello".lastIndexOf("l") // 3 |
✓ (v0.4.0) | ✓ (v0.10.0) | ✓ (v0.2.0) | ✓ (v0.1.1) | ✗ |
join |
Concatenates strings. Signatures: list(string).join([separator]) -> stringExamples: ["a", "b"].join("-") // "a-b" |
✓ (v0.10.0) | ✓ (v0.10.0) | ✓ (v0.2.0) | ✓ (v0.1.1) | ✗ |
split |
Splits string by separator. Signatures: string.split(separator, [limit]) -> list(string)Examples: "a-b".split("-") // ["a", "b"] |
✓ (v0.4.0) | ✓ (v0.10.0) | ✓ (v0.2.0) | ✓ (v0.1.1) | ✗ |
substring |
Returns substring (start inclusive, end exclusive). Signatures: string.substring(start, [end]) -> stringExamples: "hello".substring(1, 3) // "el" |
✓ (v0.4.0) | ✓ (v0.10.0) | ✓ (v0.2.0) | ✓ (v0.1.1) | ✗ |
trim |
Trims Unicode whitespace. Signatures: string.trim() -> stringExamples: " hello ".trim() // "hello" |
✓ (v0.4.0) | ✓ (v0.10.0) | ✓ (v0.2.0) | ✓ (v0.1.1) | ✗ |
replace |
Replaces occurrences of old with new. Signatures: string.replace(old, new, [limit]) -> stringExamples: "hello".replace("l", "w") // "hewwo" |
✓ (v0.4.0) | ✓ (v0.10.0) | ✓ (v0.2.0) | ✓ (v0.1.1) | ✗ |
reverse |
Reverses Unicode code points. Signatures: string.reverse() -> stringExamples: "abc".reverse() // "cba" |
✓ (v0.18.0) | ✓ (v0.14.0) | ✓ (v0.13.0) | ✓ (v0.1.1) | ✗ |
lowerAscii |
Converts ASCII characters to lowercase. Signatures: string.lowerAscii() -> stringExamples: "Hello".lowerAscii() // "hello" |
✓ (v0.6.0) | ✓ (v0.11.0) | ✓ (v0.2.0) | ✓ (v0.1.1) | ✗ |
upperAscii |
Converts ASCII characters to uppercase. Signatures: string.upperAscii() -> stringExamples: "Hello".upperAscii() // "HELLO" |
✓ (v0.6.0) | ✓ (v0.11.0) | ✓ (v0.2.0) | ✓ (v0.1.1) | ✗ |
quote |
Escapes string for safe printing. Signatures: strings.quote(string) -> stringExamples: strings.quote("a\tb") // "\"a\\tb\"" |
✓ (v0.14.0) | ✓ (v0.14.0) | ✓ (v0.13.0) | ✓ (v0.1.1) | ✗ |
How to Enable
- Go: Pass
ext.Strings()tocel.NewEnv(). - C++:
- Compiler: Add
StringsCompilerLibrary()toCompilerBuilder. - Runtime: Call
RegisterStringsFunctions()onFunctionRegistry.
- Compiler: Add
- Java: Add
CelExtensions.strings()toCelCompilerandCelRuntimebuilders. - Python: Import
cel_expr_python.ext.ext_stringsand useExtStrings()incel.NewEnv(extensions=[...]).
Regular Expression Library
| Function | Description | Go | C++ | Java | Python | C |
|---|---|---|---|---|---|---|
regex.replace |
Replaces matches with replacement string (supports backreferences). Signatures: regex.replace(target, pat, repl, [limit]) -> stringExamples: regex.replace("123-456", r"(\d+)-(\d+)", r"\2-\1") // "456-123" |
✓ (v0.25.1) | ✓ (v0.13.0) | ✓ (v0.10.1) | ✓ (v0.1.1) | ✗ |
regex.extract |
Returns first match of pattern (must have one capture group). Signatures: regex.extract(target, pat) -> optional(string)Examples: regex.extract("a123b", r"(\d+)") // optional("123") |
✓ (v0.25.1) | ✓ (v0.13.0) | ✓ (v0.10.1) | ✓ (v0.1.1) | ✗ |
regex.extractAll |
Returns all matches of pattern (must have one capture group). Signatures: regex.extractAll(target, pat) -> list(string)Examples: regex.extractAll("a1b2", r"(\d+)") // ["1", "2"] |
✓ (v0.25.1) | ✓ (v0.13.0) | ✓ (v0.10.1) | ✓ (v0.1.1) | ✗ |
How to Enable
- Go: Pass
ext.Regex()tocel.NewEnv(). - C++:
- Compiler: Add
RegexExtCompilerLibrary()toCompilerBuilder. - Runtime: Call
RegisterRegexExtensionFunctions()onFunctionRegistry.
- Compiler: Add
- Java: Add
CelExtensions.regex()toCelCompilerandCelRuntimebuilders. - Python: Enable via
cel.EnvConfigby addingregexandoptionalto theextensionslist.
Two-Variable Comprehensions
| Macro | Description | Go | C++ | Java | Python | C |
|---|---|---|---|---|---|---|
all |
Short-circuiting logical AND over key/index and value. Signatures: list.all(i, v, pred) -> boolmap.all(k, v, pred) -> boolExamples: [1, 2].all(i, v, v > 0) // true |
✓ (v0.22.0) | ✓ (v0.14.0) | ✓ (v0.11.0) | ✓ (v0.1.1) | ✗ |
exists |
Short-circuiting logical OR over key/index and value. Signatures: list.exists(i, v, pred) -> boolmap.exists(k, v, pred) -> boolExamples: [1, 2].exists(i, v, v == 2) // true |
✓ (v0.22.0) | ✓ (v0.14.0) | ✓ (v0.11.0) | ✓ (v0.1.1) | ✗ |
existsOne |
Checks if exactly one pair satisfies predicate. Signatures: list.existsOne(i, v, pred) -> boolmap.existsOne(k, v, pred) -> boolExamples: [1, 2].existsOne(i, v, v == 2) // true |
✓ (v0.22.0) | ✓ (v0.14.0) | ✓ (v0.11.0) | ✓ (v0.1.1) | ✗ |
transformList |
Transforms/filters list/map into a list. Signatures: list.transformList(i, v, [filter], transform) -> listmap.transformList(k, v, [filter], transform) -> listExamples: [1, 2].transformList(i, v, v * 2) // [2, 4] |
✓ (v0.22.0) | ✓ (v0.14.0) | ✓ (v0.11.0) | ✓ (v0.1.1) | ✗ |
transformMap |
Transforms values of list/map into a map (keys remain fixed). Signatures: list.transformMap(i, v, [filter], transform) -> mapmap.transformMap(k, v, [filter], transform) -> mapExamples: [1, 2].transformMap(i, v, v * 2) // {0: 2, 1: 4} |
✓ (v0.22.0) | ✓ (v0.14.0) | ✓ (v0.11.0) | ✓ (v0.1.1) | ✗ |
transformMapEntry |
Transforms into a map. Signatures: list.transformMapEntry(i, v, [filter], transform_entry) -> mapmap.transformMapEntry(k, v, [filter], transform_entry) -> mapExamples: [1, 2].transformMapEntry(i, v, {string(v): v * 2}) // {"1": 2, "2": 4} |
✓ (v0.22.0) | ✓ (v0.14.0) | ✓ (v0.11.0) | ✓ (v0.1.1) | ✗ |
How to Enable
- Go: Pass
ext.TwoVarComprehensions()tocel.NewEnv(). - C++:
- Compiler: Add
ComprehensionsV2CompilerLibrary()toCompilerBuilder. - Runtime: Call
RegisterComprehensionsV2Functions()onFunctionRegistryandRegisterComprehensionsV2Macros()onMacroRegistry.
- Compiler: Add
- Java: Add
CelExtensions.comprehensions()toCelCompilerandCelRuntimebuilders. - Python: Enable via
cel.EnvConfigby addingtwo-var-comprehensionsto theextensionslist.
Native Types Library
| Feature | Description | Go | C++ | Java | Python | C |
|---|---|---|---|---|---|---|
| Native Structs | Registering and instantiating host native types (Go structs / Java POJOs) in CEL. Examples: Account{id: 123} (Java POJO instanced in CEL) |
✓ (v0.13.0) | ✗ | ✓ (v0.13.0) | ✗ | ✗ |
How to Enable
- Go: Pass
ext.NativeTypes(...)(providing reflect types) tocel.NewEnv(). - C++: Not supported.
- Java: Add
CelExtensions.nativeTypes()(providing Java classes) toCelCompilerandCelRuntimebuilders. - Python: Not supported.
Network Library
The Network library provides functions for parsing, validating, and manipulating IP addresses and CIDR blocks.
| Function | Description | Go | C++ | Java | Python | C |
|---|---|---|---|---|---|---|
ip |
Parses a string into an IP address, or extracts the IP from a CIDR. Signatures: ip(string) -> IPCIDR.ip() -> IPExamples: ip("192.168.0.1")cidr("192.168.0.0/24").ip() |
✓ (v0.29.0) | ✗ | ✗ | ✗ | ✗ |
isIP |
Checks if a string is a valid IP address. Signatures: isIP(string) -> boolExamples: isIP("192.168.0.1") // true |
✓ (v0.29.0) | ✗ | ✗ | ✗ | ✗ |
ip.isCanonical |
Checks if an IP address string is in its canonical format. Signatures: ip.isCanonical(string) -> boolExamples: ip.isCanonical("192.168.0.1") // true |
✓ (v0.29.0) | ✗ | ✗ | ✗ | ✗ |
cidr |
Parses a string into a CIDR block. Signatures: cidr(string) -> CIDRExamples: cidr("192.168.0.0/24") |
✓ (v0.29.0) | ✗ | ✗ | ✗ | ✗ |
isCIDR |
Checks if a string is a valid CIDR block. Signatures: isCIDR(string) -> boolExamples: isCIDR("192.168.0.0/24") // true |
✓ (v0.29.0) | ✗ | ✗ | ✗ | ✗ |
containsIP |
Checks if a CIDR block contains an IP address. Signatures: CIDR.containsIP(IP) -> boolCIDR.containsIP(string) -> boolExamples: cidr("192.168.0.0/24").containsIP(ip("192.168.0.1")) // true |
✓ (v0.29.0) | ✗ | ✗ | ✗ | ✗ |
containsCIDR |
Checks if a CIDR block contains another CIDR block. Signatures: CIDR.containsCIDR(CIDR) -> boolCIDR.containsCIDR(string) -> boolExamples: cidr("192.168.0.0/16").containsCIDR(cidr("192.168.1.0/24")) // true |
✓ (v0.29.0) | ✗ | ✗ | ✗ | ✗ |
family |
Returns the IP family (4 for IPv4, 6 for IPv6). Signatures: IP.family() -> intExamples: ip("192.168.0.1").family() // 4 |
✓ (v0.29.0) | ✗ | ✗ | ✗ | ✗ |
isGlobalUnicast |
Checks if the IP is a global unicast address. Signatures: IP.isGlobalUnicast() -> boolExamples: ip("192.168.0.1").isGlobalUnicast() // true |
✓ (v0.29.0) | ✗ | ✗ | ✗ | ✗ |
isLinkLocalMulticast |
Checks if the IP is a link-local multicast address. Signatures: IP.isLinkLocalMulticast() -> boolExamples: ip("224.0.0.1").isLinkLocalMulticast() // true |
✓ (v0.29.0) | ✗ | ✗ | ✗ | ✗ |
isLinkLocalUnicast |
Checks if the IP is a link-local unicast address. Signatures: IP.isLinkLocalUnicast() -> boolExamples: ip("169.254.0.1").isLinkLocalUnicast() // true |
✓ (v0.29.0) | ✗ | ✗ | ✗ | ✗ |
isLoopback |
Checks if the IP is a loopback address. Signatures: IP.isLoopback() -> boolExamples: ip("127.0.0.1").isLoopback() // true |
✓ (v0.29.0) | ✗ | ✗ | ✗ | ✗ |
isMask |
Checks if the CIDR is a valid subnet mask. Signatures: CIDR.isMask() -> boolExamples: cidr("255.255.255.0/24").isMask() // true |
✓ (v0.29.0) | ✗ | ✗ | ✗ | ✗ |
isUnspecified |
Checks if the IP is an unspecified address (e.g. 0.0.0.0).Signatures: IP.isUnspecified() -> boolExamples: ip("0.0.0.0").isUnspecified() // true |
✓ (v0.29.0) | ✗ | ✗ | ✗ | ✗ |
masked |
Returns the masked CIDR block. Signatures: CIDR.masked() -> CIDRExamples: cidr("192.168.0.1/24").masked() // 192.168.0.0/24 |
✓ (v0.29.0) | ✗ | ✗ | ✗ | ✗ |
prefixLength |
Returns the prefix length of the CIDR block. Signatures: CIDR.prefixLength() -> intExamples: cidr("192.168.0.0/24").prefixLength() // 24 |
✓ (v0.29.0) | ✗ | ✗ | ✗ | ✗ |
string |
Converts IP or CIDR to string. Signatures: string(IP) -> stringstring(CIDR) -> stringExamples: string(ip("192.168.0.1")) // "192.168.0.1" |
✓ (v0.29.0) | ✗ | ✗ | ✗ | ✗ |
How to Enable
- Go: Pass
ext.Network()tocel.NewEnv(). - C++: Not supported.
- Java: Not supported.
- Python: Not supported.
5. Advanced Features
Advanced Features Summary
| Feature | Description | Go | C++ | Java | Python | C |
|---|---|---|---|---|---|---|
| Partial Evaluation | Evaluate with missing inputs; returns unknowns or a simplified expression. | ✓³ | ✓⁴ | ✓⁴ | ✗ | ✗ |
| Async Evaluation | Non-blocking concurrent execution of extension functions. | ✓⁵ | ✗ | ✓⁶ | ✗ | ✗ |
| AST Validators | Static analysis checks on the Checked AST after type-checking. | ✓ | ✓ | ✓ | ✗ | ✗ |
| AST Optimizers | AST rewrites (constant folding, inlining, CSE) to improve performance. | ✓ | ✓ | ✓ | ✗ | ✗ |
| CEL Policy Compiler | Compiles YAML-based policy structures into standard CEL ASTs. | ✓ | ✓ | ✓ | ✗ | ✗ |
³ Go supports generating a Residual AST (pruned AST). ⁴ C++ and Java support
returning UnknownSet / CelUnknownSet at runtime, but do not expose public
APIs for residual AST generation. ⁵ Go uses AsyncBinding / AsyncOp returning
channels. ⁶ Java uses CelAsyncRuntime returning ListenableFuture.
Partial Evaluation (Unknowns)
Partial evaluation allows evaluating an expression when only a subset of the input variables (arguments) are known. Instead of failing, the evaluation produces a result that indicates what is missing, or a simplified expression.
- Go: Full support. Allows defining a
PartialActivationwith patterns of unknown attributes. Evaluation returns atypes.Unknownvalue. Go supports generating a Residual AST (Env.ResidualAst) which is a pruned, simplified AST containing only the parts of the expression that could not be evaluated. - C++: Supports
Unknownvalues. Unknown attribute patterns are configured viaActivation::set_unknown_attribute_patterns. Evaluation returns anUnknownSet. Public API does not currently expose residual AST generation. - Java: Supports partial evaluation via
PartialVarspassed toProgram.eval(). Evaluation returns aCelUnknownSet. Public API does not currently expose residual AST generation. - Python / C: No native support.
Async Evaluation
Async evaluation allows CEL expressions to call functions that execute asynchronously (e.g., making RPCs or database queries) and block evaluation until the results are available, without blocking the main execution thread.
- Go: Supports asynchronous function overloads via
AsyncBindingandAsyncOp. Async functions return a Go channel (<-chan ref.Val), and the interpreter manages the concurrent execution and synchronization. - Java: Supports async evaluation via
CelAsyncRuntimeandAsyncProgram. It usesListenableFutureto represent pending values and automatically drives evaluation to completion as futures resolve. - C++ / Python / C: No built-in support.
AST Validators
Validators perform static analysis on the Checked AST after type-checking to enforce domain-specific constraints before the program is executed.
- Go: Supports
ASTValidatorinterface. Canonical validators includecel.validator.duration,cel.validator.timestamp,cel.validator.matches(regex),cel.validator.homogeneous_literals, andcel.validator.comprehension_nesting_limit. - C++: Supports
cel::Validator. Canonical validations includeAstDepthValidator,ComprehensionNestingLimitValidator,DurationLiteralValidator,HomogeneousLiteralValidator,MatchesValidator, andTimestampLiteralValidator. - Java: Supports
CelValidatorandCelAstValidator. Canonical validators includeAstDepthLimitValidator,ComprehensionNestingLimitValidator,DurationLiteralValidator,HomogeneousLiteralValidator,RegexLiteralValidator, andTimestampLiteralValidator. - Python / C: No direct support.
AST Optimizers
Optimizers rewrite the AST to improve execution performance. Optimizers fall into one of two categories: static and runtime optimizers. C++, Java, and Go support runtime optimization. CEL Java and Go also support static optimizers. Typical optimizations include constant folding (pre-evaluating sub-expressions with constant inputs) and common subexpression elimination (CSE).
- Go: Supports AST folding during compilation/planning.
- C++: Supports constant folding via the
cel::extensions::EnableConstantFoldingextension at plan time. - Java: Supports
CelOptimizerinterface. Canonical optimizers includeConstantFoldingOptimizer(which can fold short-circuiting branches by simulating partial evaluation),InliningOptimizer, andSubexpressionOptimizer(CSE). - Python / C: No direct support.
CEL Policy Compiler
CEL Policy is a YAML-based format for composing multiple CEL expressions together with variables, match blocks, conditional outputs, and nested rules. It is designed for complex policy engines (like Kubernetes Admission Control) where single CEL expressions would become unreadable.
The Policy Compiler compiles these YAML policies into a single standard CEL AST, meaning they are fully compatible with standard CEL runtimes and inherit all performance and safety guarantees.
- Go: Supported via
third_party/cel/go/policy. - C++: Supported via
third_party/cel/cpp/policy. - Java: Supported via
third_party/java/cel/policy. - Python / C: Not directly supported.