This chapter is the compact language reference for CEL expressions. It
gathers the core grammar, token categories, type names, literal forms,
operators, standard-library operations, collections, closures, and sharp edges in one
place.
CEL is an expression language. Every source form in this chapter produces
a value or helps compose one. Use the tutorial chapters for worked prose,
and use this chapter when you need the exact grammar or a concise rule
table.
These are the named cast targets. Closure annotations also accept registered cloneable leaves and built-in generic types through the configured type resolver.
Other value forms
char, byte strings, C strings, unit, tuples, arrays, ranges, closures
Expressions can use these values, but they are not extra built-in scalar type names.
Structural types
(), (T,), (T, U), [T], Fn((A, B), R)
Compose recursively without registering each aggregate. (T) groups a type. Fn((), R) has no parameters; Fn((T,), R) has one.
Unsigned integers wrap. Signed integers report arithmetic overflow on overflow. Strings concatenate.
binary -
homogeneous numeric operands
Unsigned integers wrap. Signed integers report arithmetic overflow on overflow.
unary -
signed integers and floating-point values
Signed integer overflow reports arithmetic overflow.
*
homogeneous numeric operands
Unsigned integers wrap. Signed integers report arithmetic overflow on overflow.
/, %
homogeneous numeric operands
Integer failures report division by zero.
&, |, ^
homogeneous integer operands
Integer-only bitwise operators.
<<, >>
integer left operand and integer right operand
The right operand must fit u32, and the shift count must be in range. Failures report shift overflow.
!
bool
Logical negation.
&&, ||
bool and bool
Short-circuit logical operators.
==, !=
homogeneous numeric operands, homogeneous bool, or homogeneous String
Produces bool.
<, <=, >, >=
homogeneous numeric operands or homogeneous String
Produces bool.
range forms
homogeneous numeric endpoints, or no endpoints for ..
..= requires a right endpoint. Endpoints are full expressions.
as
expression plus built-in scalar target name
Integer targets accept integer, floating-point, and bool sources. f32/f64 targets accept integer and floating-point sources. bool and String targets accept only their same-type sources.
call and .N
a valid callee and arguments, or a tuple plus an unsuffixed integer
All functions in this table belong to the standard library and are available
when the evaluation environment installs it. See
Standard library for worked examples and fuller prose.
Calls accept zero or more comma-separated arguments and do not accept a
trailing comma.
Tuple indexing uses .N with an unsuffixed integer and may be chained.
Unannotated arrays are non-empty and homogeneous. A complete type
annotation permits an empty array and must match the element type exactly.
Tuples and arrays are distinct forms and compose recursively, including
registered leaves, source unit, and callable values.
Rust interoperability uses Array<T>, source-ordered canonical lists
(Nil/Cons witnesses), and Callable<Args, R> (FnOf witness).
Array<Array<T>> converts to Vec<Array<T>>, not allocation-preserving
deep native vectors. See collections.
Arbitrary erased external values require complete shape checks.
PreparedSegment and prepared_segment::PreparedRawSegment validate at
preparation, not each invocation. Clone, equality, and formatting are
explicit independent capabilities.
Native Rust operations can serve interpreted CEL through typed adapters.
A macro-compiled CEL backend does not yet exist.
Comparison expressions consume one comparison operator.
Range forms do not chain.
expr.name and expr.name() invoke the same host-provided unary operation.
expr.name(a, b) passes the receiver first, followed by explicit arguments.
Each operand evaluates once in source order. A value binding named name
does not hide a dot operation, and the member label is not a variable reference.
(expr.name)() calls the operation’s result normally; it fails if the result
is not callable. Dot operations, ordinary calls, and tuple indexing share
postfix precedence and may be chained.
TypeName { field: expression, ... } constructs a host-registered record.
Constructor names belong to the type namespace, separate from value names.
Field labels are not variable references. Every schema field is required exactly
once; unknown, duplicate, missing, and incorrectly typed fields are errors.
Field expressions evaluate once in written order, independent of schema order.
An empty schema permits TypeName {}; a non-empty field list permits a trailing comma.
Field types match exactly: an f64 field accepts 1.0, not the i32 literal 1.
The static checker uses installed record schemas and operation signatures; known
custom types retain their identities instead of unifying with unrelated types.
Unknown constructors are errors. Unresolved inputs and calls without metadata
remain best-effort, but all their child expressions are still checked.
Unparenthesized record constructors are forbidden throughout an if condition.
Parentheses, call arguments, tuple elements, and array elements start unrestricted
expression positions: write if (Sample { value: 3.0 }.value) > 0.0 { ... }.
The restriction propagates through operators, ranges, and closure bodies.
Members are reads/operations, not assignment targets. CEL has no member assignment.
CEL does not use bracket postfix indexing such as expr[0].
CEL does not define map literals, object literals, or a ternary
operator.
A closure body resolves its own parameters and does not capture free
variables from the enclosing expression.
A one-element tuple type requires (T,); (T) is grouping.