Extensions catalog
DotnetCel.Extensions ships eight optional bundles. Each is a singleton that
adds declarations, runtime impls, and (sometimes) parser macros.
Enable with .Use(...) on the env builder:
var env = CelEnv.NewBuilder() .Use(StringsExtension.Instance) .Use(MathExtension.Instance) .Use(EncodersExtension.Instance) .Use(SetsExtension.Instance) .Use(OptionalsExtension.Instance) .Use(BindingsExtension.Instance) .Use(NetworkExtension.Instance) .Use(BlockExtension.Instance) .Build();Strings
Adds string-manipulation functions beyond the built-in startsWith,
endsWith, contains, matches.
s.charAt(i)s.indexOf(sub)s.indexOf(sub, fromIdx)s.lastIndexOf(sub)s.lastIndexOf(sub, fromIdx)s.lowerAscii()s.upperAscii()s.replace(old, new)s.replace(old, new, n)s.split(sep)s.split(sep, n)s.substring(start)s.substring(start, end)s.trim()s.format([args]) // printf-style: %s %d %f %e %x %o %bs.matches(regex)s.reverse()s.quote()strings.join(parts)strings.join(parts, sep)The format directives (%s, %d, %f, %e, %x, %X, %o, %b,
%t) follow the cel-spec; locale is fixed to en-US so output is
deterministic. See the conformance status for
which directives are at 100%.
Math
Numeric helpers, parametric over int / uint / double where it
makes sense.
math.abs(x)math.sign(x)math.greatest(a, b, ...) // 1–5 args, plus list formmath.least(a, b, ...) // 1–5 args, plus list formmath.ceil(x)math.floor(x)math.round(x)math.trunc(x)math.sqrt(x)math.isNaN(x)math.isInf(x)math.isFinite(x)math.bitAnd(a, b)math.bitOr(a, b)math.bitXor(a, b)math.bitNot(a)math.bitShiftLeft(a, n)math.bitShiftRight(a, n)Encoders
base64.encode(bytes)base64.decode(string)hex.encode(bytes)hex.decode(string)decode returns bytes; encode returns string. Errors on malformed
input.
Sets
Set predicates over lists (CEL has no separate set type — these treat lists as multisets).
sets.contains(super, sub) // ⊇sets.equivalent(a, b) // same elements (order-insensitive)sets.intersects(a, b) // ∩ ≠ ∅Optionals
The optional<T> type and its operations.
optional.of(x) // → optional<T>optional.none() // → optional<dyn>optional.ofNonZeroValue(x) // null / "" / 0 / 0u / 0.0 / [] / {} → none
opt.value() // → T (errors if empty)opt.hasValue() // → boolopt.orValue(default) // → TPlus the ?. and ?[k] operators (parsed regardless of whether the
extension is enabled, but only meaningful when it is) and the parser
macros optMap / optFlatMap.
See Optionals & null for the patterns.
Bindings
A single parser macro: cel.bind(name, init, expr).
cel.bind(square, x * x, cel.bind(double, square + square, double > 100))Internally rewritten to a comprehension whose accumulator is the binding
name. The init expression evaluates once; the body sees name bound to
its result.
Network
Network-address types and predicates. The DotnetCel.Extensions.NetworkExtension
adds ip, cidr, family, containsIP, containsCIDR.
ip("192.168.1.1") // → ipip("2001:db8::1") // → ip (v6)cidr("10.0.0.0/8") // → cidrcidr("2001:db8::/32") // → cidr (v6)
ip.family() // 4 or 6cidr.family() // 4 or 6ip.string() // canonical formcidr.string() // canonical form
cidr.contains(ip) // boolcidr.containsCIDR(otherCidr) // boolCEL types: net.IP, net.CIDR. Both are abstract types (not part of the
spec’s primitive set); they round-trip through string() and ==.
The runtime is strict about input — 192.168.1 (only 3 octets) is an
error, mixed dot/colon notation is an error.
Block
cel.@block(...) is the compiler-emitted CSE block. You don’t write it
by hand — the cel-go optimiser emits this when it identifies common
sub-expressions worth deduplicating.
The runtime resolves @index0..@indexN references via nested let-binding
scopes. Mostly there for cross-implementation consistency on programs
emitted by upstream optimisers.
Mixing extensions
Order matters in one direction: later .Use(...) calls override earlier
ones if they declare the same function name. Overload ids must remain
unique across the whole env.
If you want a “kitchen sink” env:
public static CelEnv DefaultEnv(ITypeProvider? provider = null){ var b = CelEnv.NewBuilder() .Use(StringsExtension.Instance) .Use(MathExtension.Instance) .Use(EncodersExtension.Instance) .Use(SetsExtension.Instance) .Use(OptionalsExtension.Instance) .Use(BindingsExtension.Instance) .Use(NetworkExtension.Instance) .Use(BlockExtension.Instance); if (provider is not null) b.UseTypeProvider(provider); return b.Build();}This mirrors the conformance harness’s setup and is the safest baseline for “I want everything CEL has to offer”.
See also
- Building extensions — the pattern for shipping your own.
- Parser macros — what’s inside
cel.bindand the optionals macros. - Conformance — pass-rate breakdown per extension.