本文档是通用表达式语言 (CEL) 的统一 API 文档参考。其中列出了所有宏、运算符和标准函数,并指明了它们在官方 CEL 堆栈中的签名、行为和支持状态。
如需详细了解语言行为和规范,请参阅 CEL 语言定义。
堆叠版本
本参考文档基于以下版本的 CEL 堆栈:
- CEL Go:
v0.29.2(及更高版本) - CEL C++:
v0.15.0 - CEL Java:
v0.13.1 - CEL Python:
v0.1.3 - CEL C:开发版快照(未发布)
GitHub 镜像
CEL 的官方实现镜像在 GitHub 上的 cel-expr 组织下:
1. 核心宏
这些是内置宏,会在编译时展开。
| 宏 | 说明 | Go | C++ | Java | Python | C |
|---|---|---|---|---|---|---|
has(container.field) |
测试字段是否存在于消息中,或键是否存在于映射中。 签名: has(container.field) -> bool示例: has(request.auth.claims.email) |
✓ | ✓ | ✓ | ✓ | ✓ |
list.all(var, predicate) |
测试列表中的所有元素是否都满足某个谓词。 签名: list.all(var, predicate) -> bool示例: [1, 2, 3].all(x, x > 0) // true |
✓ | ✓ | ✓ | ✓ | ✓¹ |
list.exists(var, predicate) |
测试列表中是否至少有一个元素满足谓词。 签名: list.exists(var, predicate) -> bool示例: [1, 2, 3].exists(x, x > 2) // true |
✓ | ✓ | ✓ | ✓ | ✓¹ |
list.exists_one(var, predicate) |
测试列表中是否恰好有一个元素满足谓词。 签名: list.exists_one(var, predicate) -> bool示例: [1, 2, 3].exists_one(x, x == 2) // true |
✓ | ✓ | ✓ | ✓ | ✓¹ |
list.filter(var, predicate) |
根据谓词过滤列表的元素。 签名: list.filter(var, predicate) -> list示例: [1, 2, 3].filter(x, x > 1) // [2, 3] |
✓ | ✓ | ✓ | ✓ | ✓¹ |
list.map(var, transform) |
使用表达式转换列表的每个元素。 签名: list.map(var, transform) -> list示例: [1, 2, 3].map(x, x * 2) // [2, 4, 6] |
✓ | ✓ | ✓ | ✓ | ✓¹ |
list.map(var, filter, transform) |
转换满足过滤谓词的列表元素。 签名: list.map(var, filter, transform) -> list示例: [1, 2, 3].map(x, x > 1, x * 2) // [4, 6] |
✓ | ✓ | ✓ | ✓ | ✓¹ |
¹ 在 C 运行时中受支持,因为宏在编译期间由宿主编译器扩展为推导式。
2. 核心运算符
| 运算符 | 说明 | Go | C++ | Java | Python | C |
|---|---|---|---|---|---|---|
算术(+、-、*、/、%) |
标准算术运算。取反 (-x) 和恒等 (+x)。Go、C++、Java 和 Python 支持列表串联 (list + list)。签名: T + T -> TT - T -> TT * T -> TT / T -> TT % T -> T-T -> T+T -> Tlist + list -> list示例: 1 + 2 * 3 // 7[1] + [2] // [1, 2] |
✓ | ✓ | ✓ | ✓ | ✓² |
比较(==、!=、<、<=、>、>=) |
标准比较。数值比较是异构的(例如 1 == 1.0)。签名: T == T -> boolT != T -> boolT < T -> boolT <= T -> boolT > T -> boolT >= T -> bool示例: x < 42.01 == 1.0 // true |
✓ | ✓ | ✓ | ✓ | ✓ |
逻辑(!、&&、||、? :) |
逻辑 NOT、AND、OR 和三元条件。和/或使用短路求值。 签名: !bool -> boolbool && bool -> boolbool || bool -> boolbool ? T : T -> T示例: x > 0 ? "positive" : "non-positive" |
✓ | ✓ | ✓ | ✓ | ✓ |
编入索引 ([]) |
按索引访问列表的元素,或按键查找映射中的键。 签名: list[int] -> Tmap[K] -> V示例: tags[0]users['john'] |
✓ | ✓ | ✓ | ✓ | ✓ |
会员资格 (in) |
检查元素是否在列表中,或键是否在映射中。 签名: T in list -> boolK in map -> bool示例: 'admin' in roles |
✓ | ✓ | ✓ | ✓ | ✓ |
² C 运行时不支持列表串联 (list + list),但支持其他算术运算符。
3. 核心功能
常规函数和字符串函数
| 函数 | 说明 | Go | C++ | Java | Python | C |
|---|---|---|---|---|---|---|
size |
返回字符串(字符)、字节、列表或映射的大小。 签名: size(T) -> int(其中 T 为 string、bytes、list 或 map)<br /><br />**Examples:**<br />size("hello") // 5` |
✓ | ✓ | ✓ | ✓ | ✓ |
contains |
返回字符串是否包含子字符串。 签名: string.contains(string) -> bool示例: "hello".contains("ell") // true |
✓ | ✓ | ✓ | ✓ | ✓ |
startsWith |
返回字符串是否以指定前缀开头。 签名: string.startsWith(string) -> bool示例: "hello".startsWith("he") // true |
✓ | ✓ | ✓ | ✓ | ✓ |
endsWith |
返回字符串是否以后缀结尾。 签名: string.endsWith(string) -> bool示例: "hello".endsWith("lo") // true |
✓ | ✓ | ✓ | ✓ | ✓ |
matches |
返回字符串是否与 RE2 正则表达式匹配。 签名: string.matches(string) -> bool示例: "123".matches(r"^\d+$") // true |
✓ | ✓ | ✓ | ✓ | ✓ |
日期和时间选择器函数
这些函数从 google.protobuf.Timestamp 或 google.protobuf.Duration 中提取组件。
| 函数 | 说明 | Go | C++ | Java | Python | C |
|---|---|---|---|---|---|---|
getFullYear |
返回 4 位数的年份。 签名: timestamp.getFullYear([tz]) -> int示例: timestamp("2026-07-23T00:00:00Z").getFullYear() // 2026 |
✓ | ✓ | ✓ | ✓ | ✗ |
getMonth |
返回月份(0-11)。 签名: timestamp.getMonth([tz]) -> int示例: timestamp("2026-07-23T00:00:00Z").getMonth() // 6 |
✓ | ✓ | ✓ | ✓ | ✗ |
getDayOfMonth |
返回日期(1-31)。 签名: timestamp.getDayOfMonth([tz]) -> int示例: timestamp("2026-07-23T00:00:00Z").getDayOfMonth() // 23 |
✓ | ✓ | ✓ | ✓ | ✗ |
getDayOfWeek |
返回星期几(0 表示星期日)。 签名: timestamp.getDayOfWeek([tz]) -> int示例: timestamp("2026-07-23T00:00:00Z").getDayOfWeek() // 4 |
✓ | ✓ | ✓ | ✓ | ✗ |
getDayOfYear |
返回一年中的第几天(0-365)。 签名: timestamp.getDayOfYear([tz]) -> int示例: timestamp("2026-07-23T00:00:00Z").getDayOfYear() // 203 |
✓ | ✓ | ✓ | ✓ | ✗ |
getHours |
返回小时数(0-23)。 签名: timestamp.getHours([tz]) -> intduration.getHours() -> int示例: duration("1h30m").getHours() // 1 |
✓ | ✓ | ✓ | ✓ | ✗ |
getMinutes |
返回分钟数(0-59)。 签名: timestamp.getMinutes([tz]) -> intduration.getMinutes() -> int示例: duration("1h30m").getMinutes() // 30 |
✓ | ✓ | ✓ | ✓ | ✗ |
getSeconds |
返回秒数(0-59)。 签名: timestamp.getSeconds([tz]) -> intduration.getSeconds() -> int示例: duration("1h30m45s").getSeconds() // 45 |
✓ | ✓ | ✓ | ✓ | ✗ |
getMilliseconds |
返回毫秒数(0-999)。 签名: timestamp.getMilliseconds([tz]) -> intduration.getMilliseconds() -> int示例: duration("1.5s").getMilliseconds() // 500 |
✓ | ✓ | ✓ | ✓ | ✗ |
类型转换
| 目标类型 | 说明 | Go | C++ | Java | Python | C |
|---|---|---|---|---|---|---|
bool |
转换为布尔值。 签名: bool(bool) -> boolbool(string) -> bool示例: bool("true") // true |
✓ | ✓ | ✓ | ✓ | ✓ |
bytes |
转换为字节。 签名: bytes(bytes) -> bytesbytes(string) -> bytes示例: bytes("hello") // b"hello" |
✓ | ✓ | ✓ | ✓ | ✓ |
double |
转换为双精度浮点数。 签名: double(double) -> doubledouble(int) -> doubledouble(uint) -> doubledouble(string) -> double示例: double(1) // 1.0 |
✓ | ✓ | ✓ | ✓ | ✓ |
duration |
转换为时长。 签名: duration(duration) -> durationduration(string) -> duration示例: duration("1.5s") // 1.5s duration |
✓ | ✓ | ✓ | ✓ | ✓ |
int |
转换为 64 位有符号整数。 签名: int(int) -> intint(uint) -> intint(double) -> int(四舍五入到零)int(string) -> intint(timestamp) -> int(自纪元以来的秒数)示例: int(1.5) // 1 |
✓ | ✓ | ✓ | ✓ | ✓ |
string |
转换为字符串。 签名: string(T) -> string(支持 bool、int、uint、double、bytes、timestamp、duration)<br /><br />**Examples:**<br />string(1.5) // "1.5"` |
✓ | ✓ | ✓ | ✓ | ✓ |
timestamp |
转换为时间戳。 签名: timestamp(timestamp) -> timestamptimestamp(string) -> timestamp (RFC3339)示例: timestamp("2026-07-23T00:00:00Z") |
✓ | ✓ | ✓ | ✓ | ✓ |
uint |
转换为 64 位无符号整数。 签名: uint(uint) -> uintuint(int) -> uintuint(double) -> uintuint(string) -> uint示例: uint(1) // 1u |
✓ | ✓ | ✓ | ✓ | ✓ |
dyn |
将值强制转换为动态类型以进行类型检查。 签名: dyn(T) -> dyn示例: dyn([1, "two"]) |
✓ | ✓ | ✓ | ✓ | ✗ |
type |
返回值的类型。 签名: type(T) -> type示例: type(1) // int |
✓ | ✓ | ✓ | ✓ | ✗ |
4. 扩展程序(库)
绑定库
| 函数 | 说明 | Go | C++ | Java | Python | C |
|---|---|---|---|---|---|---|
cel.bind |
绑定局部变量以避免重复评估。 签名: cel.bind(varName, initExpr, resultExpr) -> T示例: cel.bind(x, a + b, x * x) |
✓ (v0.15.0) | ✓ (v0.10.0) | ✓ (v0.2.0) | ✓ (v0.1.1) | ✗ |
如何启用
- Go:将
ext.Bindings()传递给cel.NewEnv()。 - C++:向
CompilerBuilder添加了BindingsCompilerLibrary()。(运行时由系统自动处理)。 - Java:将
CelExtensions.bindings()添加到CelCompiler和CelRuntime构建器。 - Python:导入
cel_expr_python.ext.ext_bindings并在cel.NewEnv(extensions=[...])中使用ExtBindings()。
编码器库
| 函数 | 说明 | Go | C++ | Java | Python | C |
|---|---|---|---|---|---|---|
base64.encode |
将字节编码为 base64 字符串。 签名: base64.encode(bytes) -> string示例: base64.encode(b"hello") // "aGVsbG8=" |
✓ (v0.6.0) | ✓ (v0.10.0) | ✓ (v0.2.0) | ✓ (v0.1.1) | ✗ |
base64.decode |
将 base64 字符串解码为字节。在输入无效时抛出错误。 签名: base64.decode(string) -> bytes示例: base64.decode("aGVsbG8=") // b"hello" |
✓ (v0.6.0) | ✓ (v0.10.0) | ✓ (v0.2.0) | ✓ (v0.1.1) | ✗ |
json.encode |
将 CEL 值序列化为 JSON 字符串。 签名: json.encode(dyn) -> string示例: json.encode([1, 2]) // "[1,2]" |
✓ (v0.29.0) | ✗ | ✗ | ✗ | ✗ |
如何启用
- Go:将
ext.Encoders()传递给cel.NewEnv()。 - C++:
- 编译器:向
CompilerBuilder添加了EncodersCompilerLibrary()。 - 运行时:在
FunctionRegistry上调用RegisterEncodersFunctions()。
- 编译器:向
- Java:将
CelExtensions.encoders()添加到CelCompiler和CelRuntime构建器。 - Python:导入
cel_expr_python.ext.ext_encoders并在cel.NewEnv(extensions=[...])中使用ExtEncoders()。
数学库
| 函数 | 说明 | Go | C++ | Java | Python | C |
|---|---|---|---|---|---|---|
math.greatest |
返回数值实参(或数值列表)中的最大值。 签名: math.greatest(arg, ...) -> T示例: math.greatest(1, 3, 2) // 3 |
✓ (v0.13.0) | ✓ (v0.10.0) | ✓ (v0.2.0) | ✓ (v0.1.1) | ✗ |
math.least |
返回数值实参(或数值列表)中的最小值。 签名: math.least(arg, ...) -> T示例: math.least([1, 3, 2]) // 1 |
✓ (v0.13.0) | ✓ (v0.10.0) | ✓ (v0.2.0) | ✓ (v0.1.1) | ✗ |
math.abs |
绝对值。 签名: math.abs(T) -> T(支持 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 |
平方根。 签名: math.sqrt(T) -> double(支持 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 |
按位 AND。 签名: math.bitAnd(T, T) -> T(支持 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 |
按位 OR。 签名: math.bitOr(T, T) -> T(支持 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 |
按位异或。 签名: math.bitXor(T, T) -> T(支持 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 |
按位 NOT。 签名: math.bitNot(T) -> T(支持 int、uint)<br /><br />**Examples:**<br />math.bitNot(1) // -2` |
✓ (v0.21.0) | ✓ (v0.11.0) | ✓ (v0.10.1) | ✓ (v0.1.1) | ✗ |
math.bitShiftLeft |
按位左移。 签名: math.bitShiftLeft(T, int) -> T(支持 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 |
按位右移。 签名: math.bitShiftRight(T, int) -> T(支持 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 |
向上舍入。 签名: math.ceil(double) -> double示例: math.ceil(1.2) // 2.0 |
✓ (v0.21.0) | ✓ (v0.11.0) | ✓ (v0.10.1) | ✓ (v0.1.1) | ✗ |
math.floor |
向下舍入。 签名: math.floor(double) -> double示例: math.floor(1.8) // 1.0 |
✓ (v0.21.0) | ✓ (v0.11.0) | ✓ (v0.10.1) | ✓ (v0.1.1) | ✗ |
math.round |
舍入到最接近的整数。 签名: math.round(double) -> double示例: math.round(1.5) // 2.0 |
✓ (v0.21.0) | ✓ (v0.11.0) | ✓ (v0.10.1) | ✓ (v0.1.1) | ✗ |
math.trunc |
截断舍入(向零靠拢)。 签名: math.trunc(double) -> double示例: math.trunc(-1.8) // -1.0 |
✓ (v0.21.0) | ✓ (v0.11.0) | ✓ (v0.10.1) | ✓ (v0.1.1) | ✗ |
math.isInf |
检查 double 是否为正无穷大或负无穷大。 签名: math.isInf(double) -> bool示例: math.isInf(1.0/0.0) // true |
✓ (v0.21.0) | ✓ (v0.11.0) | ✓ (v0.10.1) | ✓ (v0.1.1) | ✗ |
math.isNaN |
检查 double 是否为 NaN。 签名: math.isNaN(double) -> bool示例: math.isNaN(0.0/0.0) // true |
✓ (v0.21.0) | ✓ (v0.11.0) | ✓ (v0.10.1) | ✓ (v0.1.1) | ✗ |
math.isFinite |
检查 double 是否为有限值。 签名: math.isFinite(double) -> bool示例: math.isFinite(1.2) // true |
✓ (v0.21.0) | ✓ (v0.11.0) | ✓ (v0.10.1) | ✓ (v0.1.1) | ✗ |
math.sign |
返回值的符号(-1、0 或 1)。 签名: math.sign(T) -> T(支持 int、uint、double)<br /><br />**Examples:**<br />math.sign(-42) // -1` |
✓ (v0.21.0) | ✓ (v0.11.0) | ✓ (v0.10.1) | ✓ (v0.1.1) | ✗ |
如何启用
- Go:将
ext.Math()传递给cel.NewEnv()。 - C++:
- 编译器:向
CompilerBuilder添加了MathCompilerLibrary()。 - 运行时:在
FunctionRegistry上调用RegisterMathExtensionFunctions()。
- 编译器:向
- Java:将
CelExtensions.math()添加到CelCompiler和CelRuntime构建器。 - Python:导入
cel_expr_python.ext.ext_math并在cel.NewEnv(extensions=[...])中使用ExtMath()。
Protos 库
| 函数 | 说明 | Go | C++ | Java | Python | C |
|---|---|---|---|---|---|---|
proto.getExt |
获取 proto2 扩展字段,如果未设置,则获取默认值。 签名: proto.getExt(msg, extName) -> T示例: proto.getExt(msg, google.api.expr.test.int32_ext) |
✓ (v0.13.0) | ✓ (v0.10.0) | ✓ (v0.2.0) | ✓ (v0.1.1) | ✗ |
proto.hasExt |
检查是否设置了 proto2 扩展字段。 签名: proto.hasExt(msg, extName) -> bool示例: proto.hasExt(msg, google.api.expr.test.int32_ext) |
✓ (v0.13.0) | ✓ (v0.10.0) | ✓ (v0.2.0) | ✓ (v0.1.1) | ✗ |
如何启用
- Go:将
ext.Protos()传递给cel.NewEnv()。 - C++:向
CompilerBuilder添加了ProtoExtCompilerLibrary()。 (运行时由系统自动处理)。 - Java:将
CelExtensions.protos()添加到CelCompiler和CelRuntime构建器。 - Python:导入
cel_expr_python.ext.ext_proto并在cel.NewEnv(extensions=[...])中使用ExtProto()。
列出媒体库
| 函数 | 说明 | Go | C++ | Java | Python | C |
|---|---|---|---|---|---|---|
distinct |
返回不同的元素。 签名: list.distinct() -> list示例: [1, 2, 2].distinct() // [1, 2] |
✓ (v0.22.0) | ✓ (v0.11.0) | ✓ (v0.11.0) | ✓ (v0.1.1) | ✗ |
flatten |
展平嵌套列表。 签名: list.flatten([depth]) -> list示例: [[1], [2, 3]].flatten() // [1, 2, 3] |
✓ (v0.22.0) | ✓ (v0.11.0) | ✓ (v0.7.1) | ✓ (v0.1.1) | ✗ |
lists.range |
返回整数列表 [0, ..., n-1]。签名: lists.range(int) -> list(int)示例: lists.range(3) // [0, 1, 2] |
✓ (v0.22.0) | ✓ (v0.11.0) | ✓ (v0.10.1) | ✓ (v0.1.1) | ✗ |
reverse |
反转列表。 签名: list.reverse() -> list示例: [1, 2].reverse() // [2, 1] |
✓ (v0.22.0) | ✓ (v0.11.0) | ✓ (v0.11.0) | ✓ (v0.1.1) | ✗ |
slice |
返回子列表(包含起始值,但不含结束值)。 签名: list.slice(start, end) -> list示例: [1, 2, 3].slice(1, 3) // [2, 3] |
✓ (v0.17.0) | ✓ (v0.11.0) | ✓ (v0.11.0) | ✓ (v0.1.1) | ✗ |
sort |
对可比较元素的列表进行排序。 签名: list.sort() -> list示例: [3, 1, 2].sort() // [1, 2, 3] |
✓ (v0.22.0) | ✓ (v0.11.0) | ✓ (v0.11.0) | ✓ (v0.1.1) | ✗ |
sortBy |
按根据表达式评估的键对列表进行排序。 签名: list.sortBy(var, expr) -> list示例: [{"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 |
以可选形式返回第一个元素。需要使用可选扩展服务。 签名: list.first() -> optional示例: [1, 2].first() // optional(1) |
✓ (v0.23.0) | ✓ (v0.15.0) | ✓ (v0.11.0) | ✓ (v0.1.2) | ✗ |
last |
以可选形式返回最后一个元素。需要使用可选扩展服务。 签名: list.last() -> optional示例: [1, 2].last() // optional(2) |
✓ (v0.23.0) | ✓ (v0.15.0) | ✓ (v0.11.0) | ✓ (v0.1.2) | ✗ |
如何启用
- Go:将
ext.Lists()传递给cel.NewEnv()。 - C++:
- 编译器:向
CompilerBuilder添加了ListsCompilerLibrary()。 - 运行时:在
FunctionRegistry上调用RegisterListsFunctions(),在MacroRegistry上调用RegisterListsMacros()。
- 编译器:向
- Java:将
CelExtensions.lists()添加到CelCompiler和CelRuntime构建器。 - Python:通过
cel.EnvConfig启用,方法是将lists添加到extensions列表。
套装库
| 函数 | 说明 | Go | C++ | Java | Python | C |
|---|---|---|---|---|---|---|
sets.contains |
检查 list1 是否包含 list2 的所有元素。 签名: sets.contains(list1, list2) -> bool示例: sets.contains([1, 2], [1]) // true |
✓ (v0.15.0) | ✓ (v0.10.0) | ✓ (v0.6.0) | ✓ (v0.1.1) | ✗ |
sets.equivalent |
检查列表是否在集合上等效(包含相同的唯一元素)。 签名: sets.equivalent(list1, list2) -> bool示例: sets.equivalent([1, 2], [2, 1, 1]) // true |
✓ (v0.15.0) | ✓ (v0.10.0) | ✓ (v0.6.0) | ✓ (v0.1.1) | ✗ |
sets.intersects |
检查列表是否共用至少一个元素。 签名: sets.intersects(list1, list2) -> bool示例: sets.intersects([1, 2], [2, 3]) // true |
✓ (v0.15.0) | ✓ (v0.10.0) | ✓ (v0.6.0) | ✓ (v0.1.1) | ✗ |
如何启用
- Go:将
ext.Sets()传递给cel.NewEnv()。 - C++:
- 编译器:向
CompilerBuilder添加了SetsCompilerLibrary()。 - 运行时:在
FunctionRegistry上调用RegisterSetsFunctions()。
- 编译器:向
- Java:将
CelExtensions.sets()添加到CelCompiler和CelRuntime构建器。 - Python:通过
cel.EnvConfig启用,方法是将sets添加到extensions列表。
字符串库
| 函数 | 说明 | Go | C++ | Java | Python | C |
|---|---|---|---|---|---|---|
charAt |
返回指定索引处的字符。 签名: string.charAt(int) -> string示例: "hello".charAt(1) // "e" |
✓ (v0.4.0) | ✓ (v0.10.0) | ✓ (v0.2.0) | ✓ (v0.1.1) | ✗ |
indexOf |
返回子字符串首次出现的索引,如果未找到,则返回 -1。 签名: string.indexOf(substr, [start]) -> int示例: "hello".indexOf("l") // 2 |
✓ (v0.4.0) | ✓ (v0.10.0) | ✓ (v0.2.0) | ✓ (v0.1.1) | ✗ |
lastIndexOf |
返回子字符串最后一次出现的索引,如果未找到,则返回 -1。 签名: string.lastIndexOf(substr, [end]) -> int示例: "hello".lastIndexOf("l") // 3 |
✓ (v0.4.0) | ✓ (v0.10.0) | ✓ (v0.2.0) | ✓ (v0.1.1) | ✗ |
join |
串联字符串。 签名: list(string).join([separator]) -> string示例: ["a", "b"].join("-") // "a-b" |
✓ (v0.10.0) | ✓ (v0.10.0) | ✓ (v0.2.0) | ✓ (v0.1.1) | ✗ |
split |
按分隔符拆分字符串。 签名: string.split(separator, [limit]) -> list(string)示例: "a-b".split("-") // ["a", "b"] |
✓ (v0.4.0) | ✓ (v0.10.0) | ✓ (v0.2.0) | ✓ (v0.1.1) | ✗ |
substring |
返回子字符串(含起始索引,不含结束索引)。 签名: string.substring(start, [end]) -> string示例: "hello".substring(1, 3) // "el" |
✓ (v0.4.0) | ✓ (v0.10.0) | ✓ (v0.2.0) | ✓ (v0.1.1) | ✗ |
trim |
去除 Unicode 空白字符。 签名: string.trim() -> string示例: " hello ".trim() // "hello" |
✓ (v0.4.0) | ✓ (v0.10.0) | ✓ (v0.2.0) | ✓ (v0.1.1) | ✗ |
replace |
将旧值替换为新值。 签名: string.replace(old, new, [limit]) -> string示例: "hello".replace("l", "w") // "hewwo" |
✓ (v0.4.0) | ✓ (v0.10.0) | ✓ (v0.2.0) | ✓ (v0.1.1) | ✗ |
reverse |
反转 Unicode 码位。 签名: string.reverse() -> string示例: "abc".reverse() // "cba" |
✓ (v0.18.0) | ✓ (v0.14.0) | ✓ (v0.13.0) | ✓ (v0.1.1) | ✗ |
lowerAscii |
将 ASCII 字符转换为小写。 签名: string.lowerAscii() -> string示例: "Hello".lowerAscii() // "hello" |
✓ (v0.6.0) | ✓ (v0.11.0) | ✓ (v0.2.0) | ✓ (v0.1.1) | ✗ |
upperAscii |
将 ASCII 字符转换为大写。 签名: string.upperAscii() -> string示例: "Hello".upperAscii() // "HELLO" |
✓ (v0.6.0) | ✓ (v0.11.0) | ✓ (v0.2.0) | ✓ (v0.1.1) | ✗ |
quote |
转义字符串以进行安全打印。 签名: strings.quote(string) -> string示例: strings.quote("a\tb") // "\"a\\tb\"" |
✓ (v0.14.0) | ✓ (v0.14.0) | ✓ (v0.13.0) | ✓ (v0.1.1) | ✗ |
如何启用
- Go:将
ext.Strings()传递给cel.NewEnv()。 - C++:
- 编译器:向
CompilerBuilder添加了StringsCompilerLibrary()。 - 运行时:在
FunctionRegistry上调用RegisterStringsFunctions()。
- 编译器:向
- Java:将
CelExtensions.strings()添加到CelCompiler和CelRuntime构建器。 - Python:导入
cel_expr_python.ext.ext_strings并在cel.NewEnv(extensions=[...])中使用ExtStrings()。
正则表达式库
| 函数 | 说明 | Go | C++ | Java | Python | C |
|---|---|---|---|---|---|---|
regex.replace |
使用替换字符串替换匹配项(支持反向引用)。 签名: regex.replace(target, pat, repl, [limit]) -> string示例: 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 |
返回模式的第一个匹配项(必须有一个捕获组)。 签名: regex.extract(target, pat) -> optional(string)示例: regex.extract("a123b", r"(\d+)") // optional("123") |
✓ (v0.25.1) | ✓ (v0.13.0) | ✓ (v0.10.1) | ✓ (v0.1.1) | ✗ |
regex.extractAll |
返回模式的所有匹配项(必须有一个捕获组)。 签名: regex.extractAll(target, pat) -> list(string)示例: regex.extractAll("a1b2", r"(\d+)") // ["1", "2"] |
✓ (v0.25.1) | ✓ (v0.13.0) | ✓ (v0.10.1) | ✓ (v0.1.1) | ✗ |
如何启用
- Go:将
ext.Regex()传递给cel.NewEnv()。 - C++:
- 编译器:向
CompilerBuilder添加了RegexExtCompilerLibrary()。 - 运行时:在
FunctionRegistry上调用RegisterRegexExtensionFunctions()。
- 编译器:向
- Java:将
CelExtensions.regex()添加到CelCompiler和CelRuntime构建器。 - Python:通过
cel.EnvConfig启用,方法是将regex和optional添加到extensions列表。
双变量推导式
| 宏 | 说明 | Go | C++ | Java | Python | C |
|---|---|---|---|---|---|---|
all |
对键/索引和值进行短路逻辑 AND 运算。 签名: list.all(i, v, pred) -> boolmap.all(k, v, pred) -> bool示例: [1, 2].all(i, v, v > 0) // true |
✓ (v0.22.0) | ✓ (v0.14.0) | ✓ (v0.11.0) | ✓ (v0.1.1) | ✗ |
exists |
对键/索引和值进行短路逻辑或运算。 签名: list.exists(i, v, pred) -> boolmap.exists(k, v, pred) -> bool示例: [1, 2].exists(i, v, v == 2) // true |
✓ (v0.22.0) | ✓ (v0.14.0) | ✓ (v0.11.0) | ✓ (v0.1.1) | ✗ |
existsOne |
检查是否恰好有一对满足谓词。 签名: list.existsOne(i, v, pred) -> boolmap.existsOne(k, v, pred) -> bool示例: [1, 2].existsOne(i, v, v == 2) // true |
✓ (v0.22.0) | ✓ (v0.14.0) | ✓ (v0.11.0) | ✓ (v0.1.1) | ✗ |
transformList |
将列表/映射转换为列表。 签名: list.transformList(i, v, [filter], transform) -> listmap.transformList(k, v, [filter], transform) -> list示例: [1, 2].transformList(i, v, v * 2) // [2, 4] |
✓ (v0.22.0) | ✓ (v0.14.0) | ✓ (v0.11.0) | ✓ (v0.1.1) | ✗ |
transformMap |
将列表/映射的值转换为映射(键保持不变)。 签名: list.transformMap(i, v, [filter], transform) -> mapmap.transformMap(k, v, [filter], transform) -> map示例: [1, 2].transformMap(i, v, v * 2) // {0: 2, 1: 4} |
✓ (v0.22.0) | ✓ (v0.14.0) | ✓ (v0.11.0) | ✓ (v0.1.1) | ✗ |
transformMapEntry |
转换为地图。 签名: list.transformMapEntry(i, v, [filter], transform_entry) -> mapmap.transformMapEntry(k, v, [filter], transform_entry) -> map示例: [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) | ✗ |
如何启用
- Go:将
ext.TwoVarComprehensions()传递给cel.NewEnv()。 - C++:
- 编译器:向
CompilerBuilder添加了ComprehensionsV2CompilerLibrary()。 - 运行时:在
FunctionRegistry上调用RegisterComprehensionsV2Functions(),在MacroRegistry上调用RegisterComprehensionsV2Macros()。
- 编译器:向
- Java:将
CelExtensions.comprehensions()添加到CelCompiler和CelRuntime构建器。 - Python:通过
cel.EnvConfig启用,方法是将two-var-comprehensions添加到extensions列表。
原生类型库
| 功能 | 说明 | Go | C++ | Java | Python | C |
|---|---|---|---|---|---|---|
| 原生结构体 | 在 CEL 中注册和实例化宿主原生类型(Go 结构体 / Java POJO)。 示例: Account{id: 123}(在 CEL 中实例化的 Java POJO) |
✓ (v0.13.0) | ✗ | ✓ (v0.13.0) | ✗ | ✗ |
如何启用
- 将
ext.NativeTypes(...)(提供反射类型)传递给cel.NewEnv()。 - C++:不支持。
- Java:将
CelExtensions.nativeTypes()(提供 Java 类)添加到CelCompiler和CelRuntime构建器。 - Python:不支持。
网络库
网络库提供用于解析、验证和操作 IP 地址和 CIDR 块的函数。
| 函数 | 说明 | Go | C++ | Java | Python | C |
|---|---|---|---|---|---|---|
ip |
将字符串解析为 IP 地址,或从 CIDR 中提取 IP。 签名: ip(string) -> IPCIDR.ip() -> IP示例: ip("192.168.0.1")cidr("192.168.0.0/24").ip() |
✓ (v0.29.0) | ✗ | ✗ | ✗ | ✗ |
isIP |
检查字符串是否为有效的 IP 地址。 签名: isIP(string) -> bool示例: isIP("192.168.0.1") // true |
✓ (v0.29.0) | ✗ | ✗ | ✗ | ✗ |
ip.isCanonical |
检查 IP 地址字符串是否采用规范格式。 签名: ip.isCanonical(string) -> bool示例: ip.isCanonical("192.168.0.1") // true |
✓ (v0.29.0) | ✗ | ✗ | ✗ | ✗ |
cidr |
将字符串解析为 CIDR 块。 签名: cidr(string) -> CIDR示例: cidr("192.168.0.0/24") |
✓ (v0.29.0) | ✗ | ✗ | ✗ | ✗ |
isCIDR |
检查字符串是否为有效的 CIDR 地址块。 签名: isCIDR(string) -> bool示例: isCIDR("192.168.0.0/24") // true |
✓ (v0.29.0) | ✗ | ✗ | ✗ | ✗ |
containsIP |
检查 CIDR 块是否包含 IP 地址。 签名: CIDR.containsIP(IP) -> boolCIDR.containsIP(string) -> bool示例: cidr("192.168.0.0/24").containsIP(ip("192.168.0.1")) // true |
✓ (v0.29.0) | ✗ | ✗ | ✗ | ✗ |
containsCIDR |
检查一个 CIDR 块是否包含另一个 CIDR 块。 签名: CIDR.containsCIDR(CIDR) -> boolCIDR.containsCIDR(string) -> bool示例: cidr("192.168.0.0/16").containsCIDR(cidr("192.168.1.0/24")) // true |
✓ (v0.29.0) | ✗ | ✗ | ✗ | ✗ |
family |
返回 IP 系列(4 表示 IPv4,6 表示 IPv6)。 签名: IP.family() -> int示例: ip("192.168.0.1").family() // 4 |
✓ (v0.29.0) | ✗ | ✗ | ✗ | ✗ |
isGlobalUnicast |
检查 IP 是否为全球单播地址。 签名: IP.isGlobalUnicast() -> bool示例: ip("192.168.0.1").isGlobalUnicast() // true |
✓ (v0.29.0) | ✗ | ✗ | ✗ | ✗ |
isLinkLocalMulticast |
检查 IP 是否为链路本地多播地址。 签名: IP.isLinkLocalMulticast() -> bool示例: ip("224.0.0.1").isLinkLocalMulticast() // true |
✓ (v0.29.0) | ✗ | ✗ | ✗ | ✗ |
isLinkLocalUnicast |
检查 IP 是否为链路本地单播地址。 签名: IP.isLinkLocalUnicast() -> bool示例: ip("169.254.0.1").isLinkLocalUnicast() // true |
✓ (v0.29.0) | ✗ | ✗ | ✗ | ✗ |
isLoopback |
检查 IP 是否为环回地址。 签名: IP.isLoopback() -> bool示例: ip("127.0.0.1").isLoopback() // true |
✓ (v0.29.0) | ✗ | ✗ | ✗ | ✗ |
isMask |
检查 CIDR 是否为有效的子网掩码。 签名: CIDR.isMask() -> bool示例: cidr("255.255.255.0/24").isMask() // true |
✓ (v0.29.0) | ✗ | ✗ | ✗ | ✗ |
isUnspecified |
检查 IP 是否为未指定的地址(例如 0.0.0.0)。签名: IP.isUnspecified() -> bool示例: ip("0.0.0.0").isUnspecified() // true |
✓ (v0.29.0) | ✗ | ✗ | ✗ | ✗ |
masked |
返回经过掩码处理的 CIDR 块。 签名: CIDR.masked() -> CIDR示例: cidr("192.168.0.1/24").masked() // 192.168.0.0/24 |
✓ (v0.29.0) | ✗ | ✗ | ✗ | ✗ |
prefixLength |
返回 CIDR 块的前缀长度。 签名: CIDR.prefixLength() -> int示例: cidr("192.168.0.0/24").prefixLength() // 24 |
✓ (v0.29.0) | ✗ | ✗ | ✗ | ✗ |
string |
将 IP 或 CIDR 转换为字符串。 签名: string(IP) -> stringstring(CIDR) -> string示例: string(ip("192.168.0.1")) // "192.168.0.1" |
✓ (v0.29.0) | ✗ | ✗ | ✗ | ✗ |
如何启用
- Go:将
ext.Network()传递给cel.NewEnv()。 - C++:不支持。
- Java:不支持。
- Python:不支持。
5. 高级功能
高级功能摘要
| 功能 | 说明 | Go | C++ | Java | Python | C |
|---|---|---|---|---|---|---|
| 部分评估 | 使用缺失的输入进行评估;返回未知值或简化表达式。 | ✓³ | ✓⁴ | ✓⁴ | ✗ | ✗ |
| 异步评估 | 扩展函数以非阻塞方式并发执行。 | ✓⁵ | ✗ | ✓⁶ | ✗ | ✗ |
| AST 验证器 | 在类型检查后对已检查的 AST 进行静态分析检查。 | ✓ | ✓ | ✓ | ✗ | ✗ |
| AST 优化器 | AST 重写(常量折叠、内联、CSE),以提高性能。 | ✓ | ✓ | ✓ | ✗ | ✗ |
| CEL 政策编译器 | 将基于 YAML 的政策结构编译为标准 CEL AST。 | ✓ | ✓ | ✓ | ✗ | ✗ |
³ Go 支持生成残差 AST(经过剪枝的 AST)。⁴ C++ 和 Java 支持在运行时返回 UnknownSet / CelUnknownSet,但不公开用于生成剩余 AST 的公共 API。⁵ Go 使用 AsyncBinding / AsyncOp 返回渠道。⁶ Java 使用 CelAsyncRuntime 返回 ListenableFuture。
部分评估(未知)
部分评估是指在仅知道部分输入变量(实参)的情况下评估表达式。评估不会失败,而是会生成指示缺失内容或简化表达式的结果。
- Go:完全支持。允许使用未知属性的模式定义
PartialActivation。评估返回一个types.Unknown值。Go 支持生成剩余 AST (Env.ResidualAst),这是一个经过剪枝的简化版 AST,仅包含无法求值的表达式部分。 - C++:支持
Unknown值。未知属性模式是通过Activation::set_unknown_attribute_patterns配置的。评估会返回UnknownSet。公共 API 目前不公开剩余 AST 生成。 - Java:支持通过传递给
Program.eval()的PartialVars进行部分评估。评估返回CelUnknownSet。公共 API 目前不公开剩余 AST 生成。 - Python / C:不提供原生支持。
异步评估
异步评估允许 CEL 表达式调用异步执行的函数(例如,发出 RPC 或数据库查询),并在结果可用之前阻塞评估,而不会阻塞主执行线程。
- Go:通过
AsyncBinding和AsyncOp支持异步函数重载。异步函数会返回一个 Go 渠道 (<-chan ref.Val),而解释器会管理并发执行和同步。 - Java:支持通过
CelAsyncRuntime和AsyncProgram进行异步评估。它使用ListenableFuture表示待处理的值,并在 future 解析时自动驱动评估完成。 - C++ / Python / C:无内置支持。
AST 验证器
验证器在类型检查后对已检查的 AST 执行静态分析,以在程序执行之前强制执行特定于网域的限制。
- Go:支持
ASTValidator接口。规范验证器包括cel.validator.duration、cel.validator.timestamp、cel.validator.matches(正则表达式)、cel.validator.homogeneous_literals和cel.validator.comprehension_nesting_limit。 - C++:支持
cel::Validator。规范验证包括AstDepthValidator、ComprehensionNestingLimitValidator、DurationLiteralValidator、HomogeneousLiteralValidator、MatchesValidator和TimestampLiteralValidator。 - Java:支持
CelValidator和CelAstValidator。规范验证器包括AstDepthLimitValidator、ComprehensionNestingLimitValidator、DurationLiteralValidator、HomogeneousLiteralValidator、RegexLiteralValidator和TimestampLiteralValidator。 - Python / C:不支持直接使用。
AST 优化器
优化器会重写 AST 以提高执行性能。优化器分为两类:静态优化器和运行时优化器。C++、Java 和 Go 支持运行时优化。CEL Java 和 Go 还支持静态优化器。典型的优化包括常量折叠(预先评估具有常量输入值的子表达式)和消除公共子表达式 (CSE)。
- Go:支持在编译/规划期间进行 AST 折叠。
- C++:在规划时通过
cel::extensions::EnableConstantFolding扩展程序支持常量折叠。 - Java:支持
CelOptimizer接口。规范优化器包括ConstantFoldingOptimizer(可通过模拟部分评估来折叠短路分支)、InliningOptimizer和SubexpressionOptimizer(CSE)。 - Python / C:不支持直接使用。
CEL 政策编译器
CEL 政策是一种基于 YAML 的格式,用于将多个 CEL 表达式与变量、匹配块、条件输出和嵌套规则组合在一起。它专为复杂的政策引擎(如 Kubernetes 准入控制)而设计,在这些引擎中,单个 CEL 表达式会变得难以理解。
政策编译器会将这些 YAML 政策编译为单个标准 CEL AST,这意味着它们与标准 CEL 运行时完全兼容,并继承所有性能和安全保证。
- Go:通过
third_party/cel/go/policy提供支持。 - C++:通过
third_party/cel/cpp/policy支持。 - Java:通过
third_party/java/cel/policy提供支持。 - Python / C:不支持直接使用。