// Copyright 2021 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////////
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.6
// protoc v5.29.3
// source: cel-go-lpm.proto
package cel
import (
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
unsafe "unsafe"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
type FuzzVariables struct {
state protoimpl.MessageState `protogen:"open.v1"`
Expr string `protobuf:"bytes,1,opt,name=expr,proto3" json:"expr,omitempty"`
Inputs map[string]string `protobuf:"bytes,2,rep,name=inputs,proto3" json:"inputs,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *FuzzVariables) Reset() {
*x = FuzzVariables{}
mi := &file_cel_go_lpm_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *FuzzVariables) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*FuzzVariables) ProtoMessage() {}
func (x *FuzzVariables) ProtoReflect() protoreflect.Message {
mi := &file_cel_go_lpm_proto_msgTypes[0]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use FuzzVariables.ProtoReflect.Descriptor instead.
func (*FuzzVariables) Descriptor() ([]byte, []int) {
return file_cel_go_lpm_proto_rawDescGZIP(), []int{0}
}
func (x *FuzzVariables) GetExpr() string {
if x != nil {
return x.Expr
}
return ""
}
func (x *FuzzVariables) GetInputs() map[string]string {
if x != nil {
return x.Inputs
}
return nil
}
var File_cel_go_lpm_proto protoreflect.FileDescriptor
const file_cel_go_lpm_proto_rawDesc = "" +
"\n" +
"\x10cel-go-lpm.proto\x12\bcelgolpm\"\x9b\x01\n" +
"\rFuzzVariables\x12\x12\n" +
"\x04expr\x18\x01 \x01(\tR\x04expr\x12;\n" +
"\x06inputs\x18\x02 \x03(\v2#.celgolpm.FuzzVariables.InputsEntryR\x06inputs\x1a9\n" +
"\vInputsEntry\x12\x10\n" +
"\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" +
"\x05value\x18\x02 \x01(\tR\x05value:\x028\x01B\x1eZ\x1cgithub.com/google/cel-go/celb\x06proto3"
var (
file_cel_go_lpm_proto_rawDescOnce sync.Once
file_cel_go_lpm_proto_rawDescData []byte
)
func file_cel_go_lpm_proto_rawDescGZIP() []byte {
file_cel_go_lpm_proto_rawDescOnce.Do(func() {
file_cel_go_lpm_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_cel_go_lpm_proto_rawDesc), len(file_cel_go_lpm_proto_rawDesc)))
})
return file_cel_go_lpm_proto_rawDescData
}
var file_cel_go_lpm_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
var file_cel_go_lpm_proto_goTypes = []any{
(*FuzzVariables)(nil), // 0: celgolpm.FuzzVariables
nil, // 1: celgolpm.FuzzVariables.InputsEntry
}
var file_cel_go_lpm_proto_depIdxs = []int32{
1, // 0: celgolpm.FuzzVariables.inputs:type_name -> celgolpm.FuzzVariables.InputsEntry
1, // [1:1] is the sub-list for method output_type
1, // [1:1] is the sub-list for method input_type
1, // [1:1] is the sub-list for extension type_name
1, // [1:1] is the sub-list for extension extendee
0, // [0:1] is the sub-list for field type_name
}
func init() { file_cel_go_lpm_proto_init() }
func file_cel_go_lpm_proto_init() {
if File_cel_go_lpm_proto != nil {
return
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_cel_go_lpm_proto_rawDesc), len(file_cel_go_lpm_proto_rawDesc)),
NumEnums: 0,
NumMessages: 2,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_cel_go_lpm_proto_goTypes,
DependencyIndexes: file_cel_go_lpm_proto_depIdxs,
MessageInfos: file_cel_go_lpm_proto_msgTypes,
}.Build()
File_cel_go_lpm_proto = out.File
file_cel_go_lpm_proto_goTypes = nil
file_cel_go_lpm_proto_depIdxs = nil
}
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package cel
import (
"fmt"
"github.com/google/cel-go/common/ast"
"github.com/google/cel-go/common/decls"
"github.com/google/cel-go/common/functions"
"github.com/google/cel-go/common/types"
"github.com/google/cel-go/common/types/ref"
celpb "cel.dev/expr"
exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1"
)
// Kind indicates a CEL type's kind which is used to differentiate quickly between simple and complex types.
type Kind = types.Kind
const (
// DynKind represents a dynamic type. This kind only exists at type-check time.
DynKind Kind = types.DynKind
// AnyKind represents a google.protobuf.Any type. This kind only exists at type-check time.
AnyKind = types.AnyKind
// BoolKind represents a boolean type.
BoolKind = types.BoolKind
// BytesKind represents a bytes type.
BytesKind = types.BytesKind
// DoubleKind represents a double type.
DoubleKind = types.DoubleKind
// DurationKind represents a CEL duration type.
DurationKind = types.DurationKind
// IntKind represents an integer type.
IntKind = types.IntKind
// ListKind represents a list type.
ListKind = types.ListKind
// MapKind represents a map type.
MapKind = types.MapKind
// NullTypeKind represents a null type.
NullTypeKind = types.NullTypeKind
// OpaqueKind represents an abstract type which has no accessible fields.
OpaqueKind = types.OpaqueKind
// StringKind represents a string type.
StringKind = types.StringKind
// StructKind represents a structured object with typed fields.
StructKind = types.StructKind
// TimestampKind represents a a CEL time type.
TimestampKind = types.TimestampKind
// TypeKind represents the CEL type.
TypeKind = types.TypeKind
// TypeParamKind represents a parameterized type whose type name will be resolved at type-check time, if possible.
TypeParamKind = types.TypeParamKind
// UintKind represents a uint type.
UintKind = types.UintKind
)
var (
// AnyType represents the google.protobuf.Any type.
AnyType = types.AnyType
// BoolType represents the bool type.
BoolType = types.BoolType
// BytesType represents the bytes type.
BytesType = types.BytesType
// DoubleType represents the double type.
DoubleType = types.DoubleType
// DurationType represents the CEL duration type.
DurationType = types.DurationType
// DynType represents a dynamic CEL type whose type will be determined at runtime from context.
DynType = types.DynType
// IntType represents the int type.
IntType = types.IntType
// NullType represents the type of a null value.
NullType = types.NullType
// StringType represents the string type.
StringType = types.StringType
// TimestampType represents the time type.
TimestampType = types.TimestampType
// TypeType represents a CEL type
TypeType = types.TypeType
// UintType represents a uint type.
UintType = types.UintType
// function references for instantiating new types.
// ListType creates an instances of a list type value with the provided element type.
ListType = types.NewListType
// MapType creates an instance of a map type value with the provided key and value types.
MapType = types.NewMapType
// NullableType creates an instance of a nullable type with the provided wrapped type.
//
// Note: only primitive types are supported as wrapped types.
NullableType = types.NewNullableType
// OptionalType creates an abstract parameterized type instance corresponding to CEL's notion of optional.
OptionalType = types.NewOptionalType
// OpaqueType creates an abstract parameterized type with a given name.
OpaqueType = types.NewOpaqueType
// ObjectType creates a type references to an externally defined type, e.g. a protobuf message type.
ObjectType = types.NewObjectType
// TypeParamType creates a parameterized type instance.
TypeParamType = types.NewTypeParamType
)
// Type holds a reference to a runtime type with an optional type-checked set of type parameters.
type Type = types.Type
// Constant creates an instances of an identifier declaration with a variable name, type, and value.
func Constant(name string, t *Type, v ref.Val) EnvOption {
return func(e *Env) (*Env, error) {
e.variables = append(e.variables, decls.NewConstant(name, t, v))
return e, nil
}
}
// Variable creates an instance of a variable declaration with a variable name and type.
func Variable(name string, t *Type) EnvOption {
return VariableWithDoc(name, t, "")
}
// VariableWithDoc creates an instance of a variable declaration with a variable name, type, and doc string.
func VariableWithDoc(name string, t *Type, doc string) EnvOption {
return func(e *Env) (*Env, error) {
e.variables = append(e.variables, decls.NewVariableWithDoc(name, t, doc))
return e, nil
}
}
// VariableDecls configures a set of fully defined cel.VariableDecl instances in the environment.
func VariableDecls(vars ...*decls.VariableDecl) EnvOption {
return func(e *Env) (*Env, error) {
for _, v := range vars {
e.variables = append(e.variables, v)
}
return e, nil
}
}
// Function defines a function and overloads with optional singleton or per-overload bindings.
//
// Using Function is roughly equivalent to calling Declarations() to declare the function signatures
// and Functions() to define the function bindings, if they have been defined. Specifying the
// same function name more than once will result in the aggregation of the function overloads. If any
// signatures conflict between the existing and new function definition an error will be raised.
// However, if the signatures are identical and the overload ids are the same, the redefinition will
// be considered a no-op.
//
// One key difference with using Function() is that each FunctionDecl provided will handle dynamic
// dispatch based on the type-signatures of the overloads provided which means overload resolution at
// runtime is handled out of the box rather than via a custom binding for overload resolution via
// Functions():
//
// - Overloads are searched in the order they are declared
// - Dynamic dispatch for lists and maps is limited by inspection of the list and map contents
//
// at runtime. Empty lists and maps will result in a 'default dispatch'
//
// - In the event that a default dispatch occurs, the first overload provided is the one invoked
//
// If you intend to use overloads which differentiate based on the key or element type of a list or
// map, consider using a generic function instead: e.g. func(list(T)) or func(map(K, V)) as this
// will allow your implementation to determine how best to handle dispatch and the default behavior
// for empty lists and maps whose contents cannot be inspected.
//
// For functions which use parameterized opaque types (abstract types), consider using a singleton
// function which is capable of inspecting the contents of the type and resolving the appropriate
// overload as CEL can only make inferences by type-name regarding such types.
func Function(name string, opts ...FunctionOpt) EnvOption {
return func(e *Env) (*Env, error) {
fn, err := decls.NewFunction(name, opts...)
if err != nil {
return nil, err
}
return FunctionDecls(fn)(e)
}
}
// OverloadSelector selects an overload associated with a given function when it returns true.
//
// Used in combination with the FunctionDecl.Subset method.
type OverloadSelector = decls.OverloadSelector
// IncludeOverloads defines an OverloadSelector which allow-lists a set of overloads by their ids.
func IncludeOverloads(overloadIDs ...string) OverloadSelector {
return decls.IncludeOverloads(overloadIDs...)
}
// ExcludeOverloads defines an OverloadSelector which deny-lists a set of overloads by their ids.
func ExcludeOverloads(overloadIDs ...string) OverloadSelector {
return decls.ExcludeOverloads(overloadIDs...)
}
// FunctionDecls provides one or more fully formed function declarations to be added to the environment.
func FunctionDecls(funcs ...*decls.FunctionDecl) EnvOption {
return func(e *Env) (*Env, error) {
var err error
for _, fn := range funcs {
if existing, found := e.functions[fn.Name()]; found {
fn, err = existing.Merge(fn)
if err != nil {
return nil, err
}
}
e.functions[fn.Name()] = fn
}
return e, nil
}
}
// FunctionOpt defines a functional option for configuring a function declaration.
type FunctionOpt = decls.FunctionOpt
// FunctionDocs provides a general usage documentation for the function.
//
// Use OverloadExamples to provide example usage instructions for specific overloads.
func FunctionDocs(docs ...string) FunctionOpt {
return decls.FunctionDocs(docs...)
}
// SingletonUnaryBinding creates a singleton function definition to be used for all function overloads.
//
// Note, this approach works well if operand is expected to have a specific trait which it implements,
// e.g. traits.ContainerType. Otherwise, prefer per-overload function bindings.
func SingletonUnaryBinding(fn functions.UnaryOp, traits ...int) FunctionOpt {
return decls.SingletonUnaryBinding(fn, traits...)
}
// SingletonBinaryImpl creates a singleton function definition to be used with all function overloads.
//
// Note, this approach works well if operand is expected to have a specific trait which it implements,
// e.g. traits.ContainerType. Otherwise, prefer per-overload function bindings.
//
// Deprecated: use SingletonBinaryBinding
func SingletonBinaryImpl(fn functions.BinaryOp, traits ...int) FunctionOpt {
return decls.SingletonBinaryBinding(fn, traits...)
}
// SingletonBinaryBinding creates a singleton function definition to be used with all function overloads.
//
// Note, this approach works well if operand is expected to have a specific trait which it implements,
// e.g. traits.ContainerType. Otherwise, prefer per-overload function bindings.
func SingletonBinaryBinding(fn functions.BinaryOp, traits ...int) FunctionOpt {
return decls.SingletonBinaryBinding(fn, traits...)
}
// SingletonFunctionImpl creates a singleton function definition to be used with all function overloads.
//
// Note, this approach works well if operand is expected to have a specific trait which it implements,
// e.g. traits.ContainerType. Otherwise, prefer per-overload function bindings.
//
// Deprecated: use SingletonFunctionBinding
func SingletonFunctionImpl(fn functions.FunctionOp, traits ...int) FunctionOpt {
return decls.SingletonFunctionBinding(fn, traits...)
}
// SingletonFunctionBinding creates a singleton function definition to be used with all function overloads.
//
// Note, this approach works well if operand is expected to have a specific trait which it implements,
// e.g. traits.ContainerType. Otherwise, prefer per-overload function bindings.
func SingletonFunctionBinding(fn functions.FunctionOp, traits ...int) FunctionOpt {
return decls.SingletonFunctionBinding(fn, traits...)
}
// DisableDeclaration disables the function signatures, effectively removing them from the type-check
// environment while preserving the runtime bindings.
func DisableDeclaration(value bool) FunctionOpt {
return decls.DisableDeclaration(value)
}
// Overload defines a new global overload with an overload id, argument types, and result type. Through the
// use of OverloadOpt options, the overload may also be configured with a binding, an operand trait, and to
// be non-strict.
//
// Note: function bindings should be commonly configured with Overload instances whereas operand traits and
// strict-ness should be rare occurrences.
func Overload(overloadID string, args []*Type, resultType *Type, opts ...OverloadOpt) FunctionOpt {
return decls.Overload(overloadID, args, resultType, opts...)
}
// MemberOverload defines a new receiver-style overload (or member function) with an overload id, argument types,
// and result type. Through the use of OverloadOpt options, the overload may also be configured with a binding,
// an operand trait, and to be non-strict.
//
// Note: function bindings should be commonly configured with Overload instances whereas operand traits and
// strict-ness should be rare occurrences.
func MemberOverload(overloadID string, args []*Type, resultType *Type, opts ...OverloadOpt) FunctionOpt {
return decls.MemberOverload(overloadID, args, resultType, opts...)
}
// OverloadOpt is a functional option for configuring a function overload.
type OverloadOpt = decls.OverloadOpt
// OverloadExamples configures an example of how to invoke the overload.
func OverloadExamples(docs ...string) OverloadOpt {
return decls.OverloadExamples(docs...)
}
// UnaryBinding provides the implementation of a unary overload. The provided function is protected by a runtime
// type-guard which ensures runtime type agreement between the overload signature and runtime argument types.
func UnaryBinding(binding functions.UnaryOp) OverloadOpt {
return decls.UnaryBinding(binding)
}
// BinaryBinding provides the implementation of a binary overload. The provided function is protected by a runtime
// type-guard which ensures runtime type agreement between the overload signature and runtime argument types.
func BinaryBinding(binding functions.BinaryOp) OverloadOpt {
return decls.BinaryBinding(binding)
}
// FunctionBinding provides the implementation of a variadic overload. The provided function is protected by a runtime
// type-guard which ensures runtime type agreement between the overload signature and runtime argument types.
func FunctionBinding(binding functions.FunctionOp) OverloadOpt {
return decls.FunctionBinding(binding)
}
// LateFunctionBinding indicates that the function has a binding which is not known at compile time.
// This is useful for functions which have side-effects or are not deterministically computable.
func LateFunctionBinding() OverloadOpt {
return decls.LateFunctionBinding()
}
// OverloadIsNonStrict enables the function to be called with error and unknown argument values.
//
// Note: do not use this option unless absoluately necessary as it should be an uncommon feature.
func OverloadIsNonStrict() OverloadOpt {
return decls.OverloadIsNonStrict()
}
// OverloadOperandTrait configures a set of traits which the first argument to the overload must implement in order to be
// successfully invoked.
func OverloadOperandTrait(trait int) OverloadOpt {
return decls.OverloadOperandTrait(trait)
}
// TypeToExprType converts a CEL-native type representation to a protobuf CEL Type representation.
func TypeToExprType(t *Type) (*exprpb.Type, error) {
return types.TypeToExprType(t)
}
// ExprTypeToType converts a protobuf CEL type representation to a CEL-native type representation.
func ExprTypeToType(t *exprpb.Type) (*Type, error) {
return types.ExprTypeToType(t)
}
// ExprDeclToDeclaration converts a protobuf CEL declaration to a CEL-native declaration, either a Variable or Function.
func ExprDeclToDeclaration(d *exprpb.Decl) (EnvOption, error) {
return AlphaProtoAsDeclaration(d)
}
// AlphaProtoAsDeclaration converts a v1alpha1.Decl value describing a variable or function into an EnvOption.
func AlphaProtoAsDeclaration(d *exprpb.Decl) (EnvOption, error) {
canonical := &celpb.Decl{}
if err := convertProto(d, canonical); err != nil {
return nil, err
}
return ProtoAsDeclaration(canonical)
}
// ProtoAsDeclaration converts a canonical celpb.Decl value describing a variable or function into an EnvOption.
func ProtoAsDeclaration(d *celpb.Decl) (EnvOption, error) {
switch d.GetDeclKind().(type) {
case *celpb.Decl_Function:
overloads := d.GetFunction().GetOverloads()
opts := make([]FunctionOpt, len(overloads))
for i, o := range overloads {
args := make([]*Type, len(o.GetParams()))
for j, p := range o.GetParams() {
a, err := types.ProtoAsType(p)
if err != nil {
return nil, err
}
args[j] = a
}
res, err := types.ProtoAsType(o.GetResultType())
if err != nil {
return nil, err
}
if o.IsInstanceFunction {
opts[i] = decls.MemberOverload(o.GetOverloadId(), args, res)
} else {
opts[i] = decls.Overload(o.GetOverloadId(), args, res)
}
}
return Function(d.GetName(), opts...), nil
case *celpb.Decl_Ident:
t, err := types.ProtoAsType(d.GetIdent().GetType())
if err != nil {
return nil, err
}
if d.GetIdent().GetValue() == nil {
return Variable(d.GetName(), t), nil
}
val, err := ast.ProtoConstantAsVal(d.GetIdent().GetValue())
if err != nil {
return nil, err
}
return Constant(d.GetName(), t, val), nil
default:
return nil, fmt.Errorf("unsupported decl: %v", d)
}
}
// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package cel
import (
"errors"
"fmt"
"math"
"sync"
"github.com/google/cel-go/checker"
chkdecls "github.com/google/cel-go/checker/decls"
"github.com/google/cel-go/common"
celast "github.com/google/cel-go/common/ast"
"github.com/google/cel-go/common/containers"
"github.com/google/cel-go/common/decls"
"github.com/google/cel-go/common/env"
"github.com/google/cel-go/common/stdlib"
"github.com/google/cel-go/common/types"
"github.com/google/cel-go/common/types/ref"
"github.com/google/cel-go/interpreter"
"github.com/google/cel-go/parser"
exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1"
"google.golang.org/protobuf/reflect/protoreflect"
)
// Source interface representing a user-provided expression.
type Source = common.Source
// Ast representing the checked or unchecked expression, its source, and related metadata such as
// source position information.
type Ast struct {
source Source
impl *celast.AST
}
// NativeRep converts the AST to a Go-native representation.
func (ast *Ast) NativeRep() *celast.AST {
if ast == nil {
return nil
}
return ast.impl
}
// Expr returns the proto serializable instance of the parsed/checked expression.
//
// Deprecated: prefer cel.AstToCheckedExpr() or cel.AstToParsedExpr() and call GetExpr()
// the result instead.
func (ast *Ast) Expr() *exprpb.Expr {
if ast == nil {
return nil
}
pbExpr, _ := celast.ExprToProto(ast.NativeRep().Expr())
return pbExpr
}
// IsChecked returns whether the Ast value has been successfully type-checked.
func (ast *Ast) IsChecked() bool {
return ast.NativeRep().IsChecked()
}
// SourceInfo returns character offset and newline position information about expression elements.
func (ast *Ast) SourceInfo() *exprpb.SourceInfo {
if ast == nil {
return nil
}
pbInfo, _ := celast.SourceInfoToProto(ast.NativeRep().SourceInfo())
return pbInfo
}
// ResultType returns the output type of the expression if the Ast has been type-checked, else
// returns chkdecls.Dyn as the parse step cannot infer the type.
//
// Deprecated: use OutputType
func (ast *Ast) ResultType() *exprpb.Type {
out := ast.OutputType()
t, err := TypeToExprType(out)
if err != nil {
return chkdecls.Dyn
}
return t
}
// OutputType returns the output type of the expression if the Ast has been type-checked, else
// returns cel.DynType as the parse step cannot infer types.
func (ast *Ast) OutputType() *Type {
if ast == nil {
return types.ErrorType
}
return ast.NativeRep().GetType(ast.NativeRep().Expr().ID())
}
// Source returns a view of the input used to create the Ast. This source may be complete or
// constructed from the SourceInfo.
func (ast *Ast) Source() Source {
if ast == nil {
return nil
}
return ast.source
}
// FormatType converts a type message into a string representation.
//
// Deprecated: prefer FormatCELType
func FormatType(t *exprpb.Type) string {
return checker.FormatCheckedType(t)
}
// FormatCELType formats a cel.Type value to a string representation.
//
// The type formatting is identical to FormatType.
func FormatCELType(t *Type) string {
return checker.FormatCELType(t)
}
// Env encapsulates the context necessary to perform parsing, type checking, or generation of
// evaluable programs for different expressions.
type Env struct {
Container *containers.Container
variables []*decls.VariableDecl
functions map[string]*decls.FunctionDecl
macros []Macro
contextProto protoreflect.MessageDescriptor
adapter types.Adapter
provider types.Provider
features map[int]bool
appliedFeatures map[int]bool
libraries map[string]SingletonLibrary
validators []ASTValidator
costOptions []checker.CostOption
// Internal parser representation
prsr *parser.Parser
prsrOpts []parser.Option
// Internal checker representation
chkMutex sync.Mutex
chk *checker.Env
chkErr error
chkOnce sync.Once
chkOpts []checker.Option
// Program options tied to the environment
progOpts []ProgramOption
}
// ToConfig produces a YAML-serializable env.Config object from the given environment.
//
// The serialized configuration value is intended to represent a baseline set of config
// options which could be used as input to an EnvOption to configure the majority of the
// environment from a file.
//
// Note: validators, features, flags, and safe-guard settings are not yet supported by
// the serialize method. Since optimizers are a separate construct from the environment
// and the standard expression components (parse, check, evalute), they are also not
// supported by the serialize method.
func (e *Env) ToConfig(name string) (*env.Config, error) {
conf := env.NewConfig(name)
// Container settings
if e.Container != containers.DefaultContainer {
conf.SetContainer(e.Container.Name())
}
for _, typeName := range e.Container.AliasSet() {
conf.AddImports(env.NewImport(typeName))
}
libOverloads := map[string][]string{}
for libName, lib := range e.libraries {
// Track the options which have been configured by a library and
// then diff the library version against the configured function
// to detect incremental overloads or rewrites.
libEnv, _ := NewCustomEnv()
libEnv, _ = Lib(lib)(libEnv)
for fnName, fnDecl := range libEnv.Functions() {
if len(fnDecl.OverloadDecls()) == 0 {
continue
}
overloads, exist := libOverloads[fnName]
if !exist {
overloads = make([]string, 0, len(fnDecl.OverloadDecls()))
}
for _, o := range fnDecl.OverloadDecls() {
overloads = append(overloads, o.ID())
}
libOverloads[fnName] = overloads
}
subsetLib, canSubset := lib.(LibrarySubsetter)
alias := ""
if aliasLib, canAlias := lib.(LibraryAliaser); canAlias {
alias = aliasLib.LibraryAlias()
libName = alias
}
if libName == "stdlib" && canSubset {
conf.SetStdLib(subsetLib.LibrarySubset())
continue
}
version := uint32(math.MaxUint32)
if versionLib, isVersioned := lib.(LibraryVersioner); isVersioned {
version = versionLib.LibraryVersion()
}
conf.AddExtensions(env.NewExtension(libName, version))
}
// If this is a custom environment without the standard env, mark the stdlib as disabled.
if conf.StdLib == nil && !e.HasLibrary("cel.lib.std") {
conf.SetStdLib(env.NewLibrarySubset().SetDisabled(true))
}
// Serialize the variables
vars := make([]*decls.VariableDecl, 0, len(e.Variables()))
stdTypeVars := map[string]*decls.VariableDecl{}
for _, v := range stdlib.Types() {
stdTypeVars[v.Name()] = v
}
for _, v := range e.Variables() {
if _, isStdType := stdTypeVars[v.Name()]; isStdType {
continue
}
vars = append(vars, v)
}
if e.contextProto != nil {
conf.SetContextVariable(env.NewContextVariable(string(e.contextProto.FullName())))
skipVariables := map[string]bool{}
fields := e.contextProto.Fields()
for i := 0; i < fields.Len(); i++ {
field := fields.Get(i)
variable, err := fieldToVariable(field)
if err != nil {
return nil, fmt.Errorf("could not serialize context field variable %q, reason: %w", field.FullName(), err)
}
skipVariables[variable.Name()] = true
}
for _, v := range vars {
if _, found := skipVariables[v.Name()]; !found {
conf.AddVariableDecls(v)
}
}
} else {
conf.AddVariableDecls(vars...)
}
// Serialize functions which are distinct from the ones configured by libraries.
for fnName, fnDecl := range e.Functions() {
if excludedOverloads, found := libOverloads[fnName]; found {
if newDecl := fnDecl.Subset(decls.ExcludeOverloads(excludedOverloads...)); newDecl != nil {
conf.AddFunctionDecls(newDecl)
}
} else {
conf.AddFunctionDecls(fnDecl)
}
}
// Serialize validators
for _, val := range e.Validators() {
// Only add configurable validators to the env.Config as all others are
// expected to be implicitly enabled via extension libraries.
if confVal, ok := val.(ConfigurableASTValidator); ok {
conf.AddValidators(confVal.ToConfig())
}
}
// Serialize features
for featID, enabled := range e.features {
featName, found := featureNameByID(featID)
if !found {
// If the feature isn't named, it isn't intended to be publicly exposed
continue
}
conf.AddFeatures(env.NewFeature(featName, enabled))
}
return conf, nil
}
// NewEnv creates a program environment configured with the standard library of CEL functions and
// macros. The Env value returned can parse and check any CEL program which builds upon the core
// features documented in the CEL specification.
//
// See the EnvOption helper functions for the options that can be used to configure the
// environment.
func NewEnv(opts ...EnvOption) (*Env, error) {
// Extend the statically configured standard environment, disabling eager validation to ensure
// the cost of setup for the environment is still just as cheap as it is in v0.11.x and earlier
// releases. The user provided options can easily re-enable the eager validation as they are
// processed after this default option.
stdOpts := append([]EnvOption{EagerlyValidateDeclarations(false)}, opts...)
env, err := getStdEnv()
if err != nil {
return nil, err
}
return env.Extend(stdOpts...)
}
// NewCustomEnv creates a custom program environment which is not automatically configured with the
// standard library of functions and macros documented in the CEL spec.
//
// The purpose for using a custom environment might be for subsetting the standard library produced
// by the cel.StdLib() function. Subsetting CEL is a core aspect of its design that allows users to
// limit the compute and memory impact of a CEL program by controlling the functions and macros
// that may appear in a given expression.
//
// See the EnvOption helper functions for the options that can be used to configure the
// environment.
func NewCustomEnv(opts ...EnvOption) (*Env, error) {
registry, err := types.NewRegistry()
if err != nil {
return nil, err
}
return (&Env{
variables: []*decls.VariableDecl{},
functions: map[string]*decls.FunctionDecl{},
macros: []parser.Macro{},
Container: containers.DefaultContainer,
adapter: registry,
provider: registry,
features: map[int]bool{},
appliedFeatures: map[int]bool{},
libraries: map[string]SingletonLibrary{},
validators: []ASTValidator{},
progOpts: []ProgramOption{},
costOptions: []checker.CostOption{},
}).configure(opts)
}
// Check performs type-checking on the input Ast and yields a checked Ast and/or set of Issues.
// If any `ASTValidators` are configured on the environment, they will be applied after a valid
// type-check result. If any issues are detected, the validators will provide them on the
// output Issues object.
//
// Either checking or validation has failed if the returned Issues value and its Issues.Err()
// value are non-nil. Issues should be inspected if they are non-nil, but may not represent a
// fatal error.
//
// It is possible to have both non-nil Ast and Issues values returned from this call: however,
// the mere presence of an Ast does not imply that it is valid for use.
func (e *Env) Check(ast *Ast) (*Ast, *Issues) {
// Construct the internal checker env, erroring if there is an issue adding the declarations.
chk, err := e.initChecker()
if err != nil {
errs := common.NewErrors(ast.Source())
errs.ReportErrorString(common.NoLocation, err.Error())
return nil, NewIssuesWithSourceInfo(errs, ast.NativeRep().SourceInfo())
}
checked, errs := checker.Check(ast.NativeRep(), ast.Source(), chk)
if len(errs.GetErrors()) > 0 {
return nil, NewIssuesWithSourceInfo(errs, ast.NativeRep().SourceInfo())
}
// Manually create the Ast to ensure that the Ast source information (which may be more
// detailed than the information provided by Check), is returned to the caller.
ast = &Ast{
source: ast.Source(),
impl: checked}
// Avoid creating a validator config if it's not needed.
if len(e.validators) == 0 {
return ast, nil
}
// Generate a validator configuration from the set of configured validators.
vConfig := newValidatorConfig()
for _, v := range e.validators {
if cv, ok := v.(ASTValidatorConfigurer); ok {
cv.Configure(vConfig)
}
}
// Apply additional validators on the type-checked result.
iss := NewIssuesWithSourceInfo(errs, ast.NativeRep().SourceInfo())
for _, v := range e.validators {
v.Validate(e, vConfig, checked, iss)
}
if iss.Err() != nil {
return nil, iss
}
return ast, nil
}
// Compile combines the Parse and Check phases CEL program compilation to produce an Ast and
// associated issues.
//
// If an error is encountered during parsing the Compile step will not continue with the Check
// phase. If non-error issues are encountered during Parse, they may be combined with any issues
// discovered during Check.
//
// Note, for parse-only uses of CEL use Parse.
func (e *Env) Compile(txt string) (*Ast, *Issues) {
return e.CompileSource(common.NewTextSource(txt))
}
// CompileSource combines the Parse and Check phases CEL program compilation to produce an Ast and
// associated issues.
//
// If an error is encountered during parsing the CompileSource step will not continue with the
// Check phase. If non-error issues are encountered during Parse, they may be combined with any
// issues discovered during Check.
//
// Note, for parse-only uses of CEL use Parse.
func (e *Env) CompileSource(src Source) (*Ast, *Issues) {
ast, iss := e.ParseSource(src)
if iss.Err() != nil {
return nil, iss
}
checked, iss2 := e.Check(ast)
if iss2.Err() != nil {
return nil, iss2
}
return checked, iss2
}
// Extend the current environment with additional options to produce a new Env.
//
// Note, the extended Env value should not share memory with the original. It is possible, however,
// that a CustomTypeAdapter or CustomTypeProvider options could provide values which are mutable.
// To ensure separation of state between extended environments either make sure the TypeAdapter and
// TypeProvider are immutable, or that their underlying implementations are based on the
// ref.TypeRegistry which provides a Copy method which will be invoked by this method.
func (e *Env) Extend(opts ...EnvOption) (*Env, error) {
chk, chkErr := e.getCheckerOrError()
if chkErr != nil {
return nil, chkErr
}
prsrOptsCopy := make([]parser.Option, len(e.prsrOpts))
copy(prsrOptsCopy, e.prsrOpts)
// The type-checker is configured with Declarations. The declarations may either be provided
// as options which have not yet been validated, or may come from a previous checker instance
// whose types have already been validated.
chkOptsCopy := make([]checker.Option, len(e.chkOpts))
copy(chkOptsCopy, e.chkOpts)
// Copy the declarations if needed.
if chk != nil {
// If the type-checker has already been instantiated, then the e.declarations have been
// validated within the chk instance.
chkOptsCopy = append(chkOptsCopy, checker.ValidatedDeclarations(chk))
}
varsCopy := make([]*decls.VariableDecl, len(e.variables))
copy(varsCopy, e.variables)
// Copy macros and program options
macsCopy := make([]parser.Macro, len(e.macros))
progOptsCopy := make([]ProgramOption, len(e.progOpts))
copy(macsCopy, e.macros)
copy(progOptsCopy, e.progOpts)
// Copy the adapter / provider if they appear to be mutable.
adapter := e.adapter
provider := e.provider
adapterReg, isAdapterReg := e.adapter.(*types.Registry)
providerReg, isProviderReg := e.provider.(*types.Registry)
// In most cases the provider and adapter will be a ref.TypeRegistry;
// however, in the rare cases where they are not, they are assumed to
// be immutable. Since it is possible to set the TypeProvider separately
// from the TypeAdapter, the possible configurations which could use a
// TypeRegistry as the base implementation are captured below.
if isAdapterReg && isProviderReg {
reg := providerReg.Copy()
provider = reg
// If the adapter and provider are the same object, set the adapter
// to the same ref.TypeRegistry as the provider.
if adapterReg == providerReg {
adapter = reg
} else {
// Otherwise, make a copy of the adapter.
adapter = adapterReg.Copy()
}
} else if isProviderReg {
provider = providerReg.Copy()
} else if isAdapterReg {
adapter = adapterReg.Copy()
}
featuresCopy := make(map[int]bool, len(e.features))
for k, v := range e.features {
featuresCopy[k] = v
}
appliedFeaturesCopy := make(map[int]bool, len(e.appliedFeatures))
for k, v := range e.appliedFeatures {
appliedFeaturesCopy[k] = v
}
funcsCopy := make(map[string]*decls.FunctionDecl, len(e.functions))
for k, v := range e.functions {
funcsCopy[k] = v
}
libsCopy := make(map[string]SingletonLibrary, len(e.libraries))
for k, v := range e.libraries {
libsCopy[k] = v
}
validatorsCopy := make([]ASTValidator, len(e.validators))
copy(validatorsCopy, e.validators)
costOptsCopy := make([]checker.CostOption, len(e.costOptions))
copy(costOptsCopy, e.costOptions)
ext := &Env{
Container: e.Container,
variables: varsCopy,
functions: funcsCopy,
macros: macsCopy,
contextProto: e.contextProto,
progOpts: progOptsCopy,
adapter: adapter,
features: featuresCopy,
appliedFeatures: appliedFeaturesCopy,
libraries: libsCopy,
validators: validatorsCopy,
provider: provider,
chkOpts: chkOptsCopy,
prsrOpts: prsrOptsCopy,
costOptions: costOptsCopy,
}
return ext.configure(opts)
}
// HasFeature checks whether the environment enables the given feature
// flag, as enumerated in options.go.
func (e *Env) HasFeature(flag int) bool {
enabled, has := e.features[flag]
return has && enabled
}
// HasLibrary returns whether a specific SingletonLibrary has been configured in the environment.
func (e *Env) HasLibrary(libName string) bool {
_, exists := e.libraries[libName]
return exists
}
// Libraries returns a list of SingletonLibrary that have been configured in the environment.
func (e *Env) Libraries() []string {
libraries := make([]string, 0, len(e.libraries))
for libName := range e.libraries {
libraries = append(libraries, libName)
}
return libraries
}
// HasFunction returns whether a specific function has been configured in the environment
func (e *Env) HasFunction(functionName string) bool {
_, ok := e.functions[functionName]
return ok
}
// Functions returns a shallow copy of the Functions, keyed by function name, that have been configured in the environment.
func (e *Env) Functions() map[string]*decls.FunctionDecl {
shallowCopy := make(map[string]*decls.FunctionDecl, len(e.functions))
for nm, fn := range e.functions {
shallowCopy[nm] = fn
}
return shallowCopy
}
// Variables returns a shallow copy of the variables associated with the environment.
func (e *Env) Variables() []*decls.VariableDecl {
shallowCopy := make([]*decls.VariableDecl, len(e.variables))
copy(shallowCopy, e.variables)
return shallowCopy
}
// Macros returns a shallow copy of macros associated with the environment.
func (e *Env) Macros() []Macro {
shallowCopy := make([]Macro, len(e.macros))
copy(shallowCopy, e.macros)
return shallowCopy
}
// HasValidator returns whether a specific ASTValidator has been configured in the environment.
func (e *Env) HasValidator(name string) bool {
for _, v := range e.validators {
if v.Name() == name {
return true
}
}
return false
}
// Validators returns the set of ASTValidators configured on the environment.
func (e *Env) Validators() []ASTValidator {
return e.validators[:]
}
// Parse parses the input expression value `txt` to a Ast and/or a set of Issues.
//
// This form of Parse creates a Source value for the input `txt` and forwards to the
// ParseSource method.
func (e *Env) Parse(txt string) (*Ast, *Issues) {
src := common.NewTextSource(txt)
return e.ParseSource(src)
}
// ParseSource parses the input source to an Ast and/or set of Issues.
//
// Parsing has failed if the returned Issues value and its Issues.Err() value is non-nil.
// Issues should be inspected if they are non-nil, but may not represent a fatal error.
//
// It is possible to have both non-nil Ast and Issues values returned from this call; however,
// the mere presence of an Ast does not imply that it is valid for use.
func (e *Env) ParseSource(src Source) (*Ast, *Issues) {
parsed, errs := e.prsr.Parse(src)
if len(errs.GetErrors()) > 0 {
return nil, &Issues{errs: errs}
}
return &Ast{source: src, impl: parsed}, nil
}
// Program generates an evaluable instance of the Ast within the environment (Env).
func (e *Env) Program(ast *Ast, opts ...ProgramOption) (Program, error) {
return e.PlanProgram(ast.NativeRep(), opts...)
}
// PlanProgram generates an evaluable instance of the AST in the go-native representation within
// the environment (Env).
func (e *Env) PlanProgram(a *celast.AST, opts ...ProgramOption) (Program, error) {
optSet := e.progOpts
if len(opts) != 0 {
mergedOpts := []ProgramOption{}
mergedOpts = append(mergedOpts, e.progOpts...)
mergedOpts = append(mergedOpts, opts...)
optSet = mergedOpts
}
return newProgram(e, a, optSet)
}
// CELTypeAdapter returns the `types.Adapter` configured for the environment.
func (e *Env) CELTypeAdapter() types.Adapter {
return e.adapter
}
// CELTypeProvider returns the `types.Provider` configured for the environment.
func (e *Env) CELTypeProvider() types.Provider {
return e.provider
}
// TypeAdapter returns the `ref.TypeAdapter` configured for the environment.
//
// Deprecated: use CELTypeAdapter()
func (e *Env) TypeAdapter() ref.TypeAdapter {
return e.adapter
}
// TypeProvider returns the `ref.TypeProvider` configured for the environment.
//
// Deprecated: use CELTypeProvider()
func (e *Env) TypeProvider() ref.TypeProvider {
if legacyProvider, ok := e.provider.(ref.TypeProvider); ok {
return legacyProvider
}
return &interopLegacyTypeProvider{Provider: e.provider}
}
// UnknownVars returns a PartialActivation which marks all variables declared in the Env as
// unknown AttributePattern values.
//
// Note, the UnknownVars will behave the same as an cel.NoVars() unless the PartialAttributes
// option is provided as a ProgramOption.
func (e *Env) UnknownVars() PartialActivation {
act := interpreter.EmptyActivation()
part, _ := PartialVars(act, e.computeUnknownVars(act)...)
return part
}
// PartialVars returns a PartialActivation where all variables not in the input variable
// set, but which have been configured in the environment, are marked as unknown.
//
// The `vars` value may either be an Activation or any valid input to the cel.NewActivation call.
//
// Note, this is equivalent to calling cel.PartialVars and manually configuring the set of unknown
// variables. For more advanced use cases of partial state where portions of an object graph, rather
// than top-level variables, are missing the PartialVars() method may be a more suitable choice.
//
// Note, the PartialVars will behave the same as cel.NoVars() unless the PartialAttributes
// option is provided as a ProgramOption.
func (e *Env) PartialVars(vars any) (PartialActivation, error) {
act, err := NewActivation(vars)
if err != nil {
return nil, err
}
return PartialVars(act, e.computeUnknownVars(act)...)
}
// ResidualAst takes an Ast and its EvalDetails to produce a new Ast which only contains the
// attribute references which are unknown.
//
// Residual expressions are beneficial in a few scenarios:
//
// - Optimizing constant expression evaluations away.
// - Indexing and pruning expressions based on known input arguments.
// - Surfacing additional requirements that are needed in order to complete an evaluation.
// - Sharing the evaluation of an expression across multiple machines/nodes.
//
// For example, if an expression targets a 'resource' and 'request' attribute and the possible
// values for the resource are known, a PartialActivation could mark the 'request' as an unknown
// interpreter.AttributePattern and the resulting ResidualAst would be reduced to only the parts
// of the expression that reference the 'request'.
//
// Note, the expression ids within the residual AST generated through this method have no
// correlation to the expression ids of the original AST.
//
// See the PartialVars helper for how to construct a PartialActivation.
//
// TODO: Consider adding an option to generate a Program.Residual to avoid round-tripping to an
// Ast format and then Program again.
func (e *Env) ResidualAst(a *Ast, details *EvalDetails) (*Ast, error) {
ast := a.NativeRep()
pruned := interpreter.PruneAst(ast.Expr(), ast.SourceInfo().MacroCalls(), details.State())
newAST := &Ast{source: a.Source(), impl: pruned}
expr, err := AstToString(newAST)
if err != nil {
return nil, err
}
parsed, iss := e.Parse(expr)
if iss != nil && iss.Err() != nil {
return nil, iss.Err()
}
if !a.IsChecked() {
return parsed, nil
}
checked, iss := e.Check(parsed)
if iss != nil && iss.Err() != nil {
return nil, iss.Err()
}
return checked, nil
}
// EstimateCost estimates the cost of a type checked CEL expression using the length estimates of input data and
// extension functions provided by estimator.
func (e *Env) EstimateCost(ast *Ast, estimator checker.CostEstimator, opts ...checker.CostOption) (checker.CostEstimate, error) {
extendedOpts := make([]checker.CostOption, 0, len(e.costOptions))
extendedOpts = append(extendedOpts, opts...)
extendedOpts = append(extendedOpts, e.costOptions...)
return checker.Cost(ast.NativeRep(), estimator, extendedOpts...)
}
// configure applies a series of EnvOptions to the current environment.
func (e *Env) configure(opts []EnvOption) (*Env, error) {
// Customized the environment using the provided EnvOption values. If an error is
// generated at any step this, will be returned as a nil Env with a non-nil error.
var err error
for _, opt := range opts {
e, err = opt(e)
if err != nil {
return nil, err
}
}
// If the default UTC timezone has been disabled, configure the legacy overloads
if utcTime, isSet := e.features[featureDefaultUTCTimeZone]; isSet && !utcTime {
if !e.appliedFeatures[featureDefaultUTCTimeZone] {
e.appliedFeatures[featureDefaultUTCTimeZone] = true
e, err = Lib(timeLegacyLibrary{})(e)
if err != nil {
return nil, err
}
}
}
// Configure the parser.
prsrOpts := []parser.Option{}
prsrOpts = append(prsrOpts, e.prsrOpts...)
prsrOpts = append(prsrOpts, parser.Macros(e.macros...))
if e.HasFeature(featureEnableMacroCallTracking) {
prsrOpts = append(prsrOpts, parser.PopulateMacroCalls(true))
}
if e.HasFeature(featureVariadicLogicalASTs) {
prsrOpts = append(prsrOpts, parser.EnableVariadicOperatorASTs(true))
}
if e.HasFeature(featureIdentEscapeSyntax) {
prsrOpts = append(prsrOpts, parser.EnableIdentEscapeSyntax(true))
}
e.prsr, err = parser.NewParser(prsrOpts...)
if err != nil {
return nil, err
}
// Ensure that the checker init happens eagerly rather than lazily.
if e.HasFeature(featureEagerlyValidateDeclarations) {
_, err := e.initChecker()
if err != nil {
return nil, err
}
}
return e, nil
}
func (e *Env) initChecker() (*checker.Env, error) {
e.chkOnce.Do(func() {
chkOpts := []checker.Option{}
chkOpts = append(chkOpts, e.chkOpts...)
chkOpts = append(chkOpts,
checker.CrossTypeNumericComparisons(
e.HasFeature(featureCrossTypeNumericComparisons)))
ce, err := checker.NewEnv(e.Container, e.provider, chkOpts...)
if err != nil {
e.setCheckerOrError(nil, err)
return
}
// Add the statically configured declarations.
err = ce.AddIdents(e.variables...)
if err != nil {
e.setCheckerOrError(nil, err)
return
}
// Add the function declarations which are derived from the FunctionDecl instances.
for _, fn := range e.functions {
if fn.IsDeclarationDisabled() {
continue
}
err = ce.AddFunctions(fn)
if err != nil {
e.setCheckerOrError(nil, err)
return
}
}
// Add function declarations here separately.
e.setCheckerOrError(ce, nil)
})
return e.getCheckerOrError()
}
// setCheckerOrError sets the checker.Env or error state in a concurrency-safe manner
func (e *Env) setCheckerOrError(chk *checker.Env, chkErr error) {
e.chkMutex.Lock()
e.chk = chk
e.chkErr = chkErr
e.chkMutex.Unlock()
}
// getCheckerOrError gets the checker.Env or error state in a concurrency-safe manner
func (e *Env) getCheckerOrError() (*checker.Env, error) {
e.chkMutex.Lock()
defer e.chkMutex.Unlock()
return e.chk, e.chkErr
}
// computeUnknownVars determines a set of missing variables based on the input activation and the
// environment's configured declaration set.
func (e *Env) computeUnknownVars(vars Activation) []*interpreter.AttributePattern {
var unknownPatterns []*interpreter.AttributePattern
for _, v := range e.variables {
varName := v.Name()
if _, found := vars.ResolveName(varName); found {
continue
}
unknownPatterns = append(unknownPatterns, interpreter.NewAttributePattern(varName))
}
return unknownPatterns
}
// Error type which references an expression id, a location within source, and a message.
type Error = common.Error
// Issues defines methods for inspecting the error details of parse and check calls.
//
// Note: in the future, non-fatal warnings and notices may be inspectable via the Issues struct.
type Issues struct {
errs *common.Errors
info *celast.SourceInfo
}
// NewIssues returns an Issues struct from a common.Errors object.
func NewIssues(errs *common.Errors) *Issues {
return NewIssuesWithSourceInfo(errs, nil)
}
// NewIssuesWithSourceInfo returns an Issues struct from a common.Errors object with SourceInfo metatata
// which can be used with the `ReportErrorAtID` method for additional error reports within the context
// information that's inferred from an expression id.
func NewIssuesWithSourceInfo(errs *common.Errors, info *celast.SourceInfo) *Issues {
return &Issues{
errs: errs,
info: info,
}
}
// Err returns an error value if the issues list contains one or more errors.
func (i *Issues) Err() error {
if i == nil {
return nil
}
if len(i.Errors()) > 0 {
return errors.New(i.String())
}
return nil
}
// Errors returns the collection of errors encountered in more granular detail.
func (i *Issues) Errors() []*Error {
if i == nil {
return []*Error{}
}
return i.errs.GetErrors()
}
// Append collects the issues from another Issues struct into a new Issues object.
func (i *Issues) Append(other *Issues) *Issues {
if i == nil {
return other
}
if other == nil || i == other {
return i
}
return NewIssuesWithSourceInfo(i.errs.Append(other.errs.GetErrors()), i.info)
}
// String converts the issues to a suitable display string.
func (i *Issues) String() string {
if i == nil {
return ""
}
return i.errs.ToDisplayString()
}
// ReportErrorAtID reports an error message with an optional set of formatting arguments.
//
// The source metadata for the expression at `id`, if present, is attached to the error report.
// To ensure that source metadata is attached to error reports, use NewIssuesWithSourceInfo.
func (i *Issues) ReportErrorAtID(id int64, message string, args ...any) {
i.errs.ReportErrorAtID(id, i.info.GetStartLocation(id), message, args...)
}
// getStdEnv lazy initializes the CEL standard environment.
func getStdEnv() (*Env, error) {
stdEnvInit.Do(func() {
stdEnv, stdEnvErr = NewCustomEnv(StdLib(), EagerlyValidateDeclarations(true))
})
return stdEnv, stdEnvErr
}
// interopCELTypeProvider layers support for the types.Provider interface on top of a ref.TypeProvider.
type interopCELTypeProvider struct {
ref.TypeProvider
}
// FindStructType returns a types.Type instance for the given fully-qualified typeName if one exists.
//
// This method proxies to the underlying ref.TypeProvider's FindType method and converts protobuf type
// into a native type representation. If the conversion fails, the type is listed as not found.
func (p *interopCELTypeProvider) FindStructType(typeName string) (*types.Type, bool) {
if et, found := p.FindType(typeName); found {
t, err := types.ExprTypeToType(et)
if err != nil {
return nil, false
}
return t, true
}
return nil, false
}
// FindStructFieldNames returns an empty set of field for the interop provider.
//
// To inspect the field names, migrate to a `types.Provider` implementation.
func (p *interopCELTypeProvider) FindStructFieldNames(typeName string) ([]string, bool) {
return []string{}, false
}
// FindStructFieldType returns a types.FieldType instance for the given fully-qualified typeName and field
// name, if one exists.
//
// This method proxies to the underlying ref.TypeProvider's FindFieldType method and converts protobuf type
// into a native type representation. If the conversion fails, the type is listed as not found.
func (p *interopCELTypeProvider) FindStructFieldType(structType, fieldName string) (*types.FieldType, bool) {
if ft, found := p.FindFieldType(structType, fieldName); found {
t, err := types.ExprTypeToType(ft.Type)
if err != nil {
return nil, false
}
return &types.FieldType{
Type: t,
IsSet: ft.IsSet,
GetFrom: ft.GetFrom,
}, true
}
return nil, false
}
// interopLegacyTypeProvider layers support for the ref.TypeProvider interface on top of a types.Provider.
type interopLegacyTypeProvider struct {
types.Provider
}
// FindType retruns the protobuf Type representation for the input type name if one exists.
//
// This method proxies to the underlying types.Provider FindStructType method and converts the types.Type
// value to a protobuf Type representation.
//
// Failure to convert the type will result in the type not being found.
func (p *interopLegacyTypeProvider) FindType(typeName string) (*exprpb.Type, bool) {
if t, found := p.FindStructType(typeName); found {
et, err := types.TypeToExprType(t)
if err != nil {
return nil, false
}
return et, true
}
return nil, false
}
// FindFieldType returns the protobuf-based FieldType representation for the input type name and field,
// if one exists.
//
// This call proxies to the types.Provider FindStructFieldType method and converts the types.FIeldType
// value to a protobuf-based ref.FieldType representation if found.
//
// Failure to convert the FieldType will result in the field not being found.
func (p *interopLegacyTypeProvider) FindFieldType(structType, fieldName string) (*ref.FieldType, bool) {
if cft, found := p.FindStructFieldType(structType, fieldName); found {
et, err := types.TypeToExprType(cft.Type)
if err != nil {
return nil, false
}
return &ref.FieldType{
Type: et,
IsSet: cft.IsSet,
GetFrom: cft.GetFrom,
}, true
}
return nil, false
}
var (
stdEnvInit sync.Once
stdEnv *Env
stdEnvErr error
)
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package cel
import (
"fmt"
"github.com/google/cel-go/common/ast"
"github.com/google/cel-go/common/operators"
"github.com/google/cel-go/common/overloads"
"github.com/google/cel-go/common/types"
"github.com/google/cel-go/common/types/ref"
"github.com/google/cel-go/common/types/traits"
)
// ConstantFoldingOption defines a functional option for configuring constant folding.
type ConstantFoldingOption func(opt *constantFoldingOptimizer) (*constantFoldingOptimizer, error)
// MaxConstantFoldIterations limits the number of times literals may be folding during optimization.
//
// Defaults to 100 if not set.
func MaxConstantFoldIterations(limit int) ConstantFoldingOption {
return func(opt *constantFoldingOptimizer) (*constantFoldingOptimizer, error) {
opt.maxFoldIterations = limit
return opt, nil
}
}
// FoldKnownValues adds an Activation which provides known values for the folding evaluator
//
// Any values the activation provides will be used by the constant folder and turned into
// literals in the AST.
//
// Defaults to the NoVars() Activation
func FoldKnownValues(knownValues Activation) ConstantFoldingOption {
return func(opt *constantFoldingOptimizer) (*constantFoldingOptimizer, error) {
if knownValues != nil {
opt.knownValues = knownValues
} else {
opt.knownValues = NoVars()
}
return opt, nil
}
}
// NewConstantFoldingOptimizer creates an optimizer which inlines constant scalar an aggregate
// literal values within function calls and select statements with their evaluated result.
func NewConstantFoldingOptimizer(opts ...ConstantFoldingOption) (ASTOptimizer, error) {
folder := &constantFoldingOptimizer{
maxFoldIterations: defaultMaxConstantFoldIterations,
}
var err error
for _, o := range opts {
folder, err = o(folder)
if err != nil {
return nil, err
}
}
return folder, nil
}
type constantFoldingOptimizer struct {
maxFoldIterations int
knownValues Activation
}
// Optimize queries the expression graph for scalar and aggregate literal expressions within call and
// select statements and then evaluates them and replaces the call site with the literal result.
//
// Note: only values which can be represented as literals in CEL syntax are supported.
func (opt *constantFoldingOptimizer) Optimize(ctx *OptimizerContext, a *ast.AST) *ast.AST {
root := ast.NavigateAST(a)
// Walk the list of foldable expression and continue to fold until there are no more folds left.
// All of the fold candidates returned by the constantExprMatcher should succeed unless there's
// a logic bug with the selection of expressions.
constantExprMatcherCapture := func(e ast.NavigableExpr) bool { return opt.constantExprMatcher(ctx, a, e) }
foldableExprs := ast.MatchDescendants(root, constantExprMatcherCapture)
foldCount := 0
for len(foldableExprs) != 0 && foldCount < opt.maxFoldIterations {
for _, fold := range foldableExprs {
// If the expression could be folded because it's a non-strict call, and the
// branches are pruned, continue to the next fold.
if fold.Kind() == ast.CallKind && maybePruneBranches(ctx, fold) {
continue
}
// Late-bound function calls cannot be folded.
if fold.Kind() == ast.CallKind && isLateBoundFunctionCall(ctx, a, fold) {
continue
}
// Otherwise, assume all context is needed to evaluate the expression.
err := opt.tryFold(ctx, a, fold)
// Ignore errors for identifiers, since there is no guarantee that the environment
// has a value for them.
if err != nil && fold.Kind() != ast.IdentKind {
ctx.ReportErrorAtID(fold.ID(), "constant-folding evaluation failed: %v", err.Error())
return a
}
}
foldCount++
foldableExprs = ast.MatchDescendants(root, constantExprMatcherCapture)
}
// Once all of the constants have been folded, try to run through the remaining comprehensions
// one last time. In this case, there's no guarantee they'll run, so we only update the
// target comprehension node with the literal value if the evaluation succeeds.
for _, compre := range ast.MatchDescendants(root, ast.KindMatcher(ast.ComprehensionKind)) {
opt.tryFold(ctx, a, compre)
}
// If the output is a list, map, or struct which contains optional entries, then prune it
// to make sure that the optionals, if resolved, do not surface in the output literal.
pruneOptionalElements(ctx, root)
// Ensure that all intermediate values in the folded expression can be represented as valid
// CEL literals within the AST structure. Use `PostOrderVisit` rather than `MatchDescendents`
// to avoid extra allocations during this final pass through the AST.
ast.PostOrderVisit(root, ast.NewExprVisitor(func(e ast.Expr) {
if e.Kind() != ast.LiteralKind {
return
}
val := e.AsLiteral()
adapted, err := adaptLiteral(ctx, val)
if err != nil {
ctx.ReportErrorAtID(root.ID(), "constant-folding evaluation failed: %v", err.Error())
return
}
ctx.UpdateExpr(e, adapted)
}))
return a
}
// tryFold attempts to evaluate a sub-expression to a literal.
//
// If the evaluation succeeds, the input expr value will be modified to become a literal, otherwise
// the method will return an error.
func (opt *constantFoldingOptimizer) tryFold(ctx *OptimizerContext, a *ast.AST, expr ast.Expr) error {
// Assume all context is needed to evaluate the expression.
subAST := &Ast{
impl: ast.NewCheckedAST(ast.NewAST(expr, a.SourceInfo()), a.TypeMap(), a.ReferenceMap()),
}
prg, err := ctx.Program(subAST)
if err != nil {
return err
}
activation := opt.knownValues
if activation == nil {
activation = NoVars()
}
out, _, err := prg.Eval(activation)
if err != nil {
return err
}
// Update the fold expression to be a literal.
ctx.UpdateExpr(expr, ctx.NewLiteral(out))
return nil
}
func isLateBoundFunctionCall(ctx *OptimizerContext, a *ast.AST, expr ast.Expr) bool {
call := expr.AsCall()
function := ctx.Functions()[call.FunctionName()]
if function == nil {
return false
}
return function.HasLateBinding()
}
// maybePruneBranches inspects the non-strict call expression to determine whether
// a branch can be removed. Evaluation will naturally prune logical and / or calls,
// but conditional will not be pruned cleanly, so this is one small area where the
// constant folding step reimplements a portion of the evaluator.
func maybePruneBranches(ctx *OptimizerContext, expr ast.NavigableExpr) bool {
call := expr.AsCall()
args := call.Args()
switch call.FunctionName() {
case operators.LogicalAnd, operators.LogicalOr:
return maybeShortcircuitLogic(ctx, call.FunctionName(), args, expr)
case operators.Conditional:
cond := args[0]
truthy := args[1]
falsy := args[2]
if cond.Kind() != ast.LiteralKind {
return false
}
if cond.AsLiteral() == types.True {
ctx.UpdateExpr(expr, truthy)
} else {
ctx.UpdateExpr(expr, falsy)
}
return true
case operators.In:
haystack := args[1]
if haystack.Kind() == ast.ListKind && haystack.AsList().Size() == 0 {
ctx.UpdateExpr(expr, ctx.NewLiteral(types.False))
return true
}
needle := args[0]
if needle.Kind() == ast.LiteralKind && haystack.Kind() == ast.ListKind {
needleValue := needle.AsLiteral()
list := haystack.AsList()
for _, e := range list.Elements() {
if e.Kind() == ast.LiteralKind && e.AsLiteral().Equal(needleValue) == types.True {
ctx.UpdateExpr(expr, ctx.NewLiteral(types.True))
return true
}
}
}
}
return false
}
func maybeShortcircuitLogic(ctx *OptimizerContext, function string, args []ast.Expr, expr ast.NavigableExpr) bool {
shortcircuit := types.False
skip := types.True
if function == operators.LogicalOr {
shortcircuit = types.True
skip = types.False
}
newArgs := []ast.Expr{}
for _, arg := range args {
if arg.Kind() != ast.LiteralKind {
newArgs = append(newArgs, arg)
continue
}
if arg.AsLiteral() == skip {
continue
}
if arg.AsLiteral() == shortcircuit {
ctx.UpdateExpr(expr, arg)
return true
}
}
if len(newArgs) == 0 {
newArgs = append(newArgs, args[0])
ctx.UpdateExpr(expr, newArgs[0])
return true
}
if len(newArgs) == 1 {
ctx.UpdateExpr(expr, newArgs[0])
return true
}
ctx.UpdateExpr(expr, ctx.NewCall(function, newArgs...))
return true
}
// pruneOptionalElements works from the bottom up to resolve optional elements within
// aggregate literals.
//
// Note, many aggregate literals will be resolved as arguments to functions or select
// statements, so this method exists to handle the case where the literal could not be
// fully resolved or exists outside of a call, select, or comprehension context.
func pruneOptionalElements(ctx *OptimizerContext, root ast.NavigableExpr) {
aggregateLiterals := ast.MatchDescendants(root, aggregateLiteralMatcher)
for _, lit := range aggregateLiterals {
switch lit.Kind() {
case ast.ListKind:
pruneOptionalListElements(ctx, lit)
case ast.MapKind:
pruneOptionalMapEntries(ctx, lit)
case ast.StructKind:
pruneOptionalStructFields(ctx, lit)
}
}
}
func pruneOptionalListElements(ctx *OptimizerContext, e ast.Expr) {
l := e.AsList()
elems := l.Elements()
optIndices := l.OptionalIndices()
if len(optIndices) == 0 {
return
}
updatedElems := []ast.Expr{}
updatedIndices := []int32{}
newOptIndex := -1
for _, e := range elems {
newOptIndex++
if !l.IsOptional(int32(newOptIndex)) {
updatedElems = append(updatedElems, e)
continue
}
if e.Kind() != ast.LiteralKind {
updatedElems = append(updatedElems, e)
updatedIndices = append(updatedIndices, int32(newOptIndex))
continue
}
optElemVal, ok := e.AsLiteral().(*types.Optional)
if !ok {
updatedElems = append(updatedElems, e)
updatedIndices = append(updatedIndices, int32(newOptIndex))
continue
}
if !optElemVal.HasValue() {
newOptIndex-- // Skipping causes the list to get smaller.
continue
}
ctx.UpdateExpr(e, ctx.NewLiteral(optElemVal.GetValue()))
updatedElems = append(updatedElems, e)
}
ctx.UpdateExpr(e, ctx.NewList(updatedElems, updatedIndices))
}
func pruneOptionalMapEntries(ctx *OptimizerContext, e ast.Expr) {
m := e.AsMap()
entries := m.Entries()
updatedEntries := []ast.EntryExpr{}
modified := false
for _, e := range entries {
entry := e.AsMapEntry()
key := entry.Key()
val := entry.Value()
// If the entry is not optional, or the value-side of the optional hasn't
// been resolved to a literal, then preserve the entry as-is.
if !entry.IsOptional() || val.Kind() != ast.LiteralKind {
updatedEntries = append(updatedEntries, e)
continue
}
optElemVal, ok := val.AsLiteral().(*types.Optional)
if !ok {
updatedEntries = append(updatedEntries, e)
continue
}
// When the key is not a literal, but the value is, then it needs to be
// restored to an optional value.
if key.Kind() != ast.LiteralKind {
undoOptVal, err := adaptLiteral(ctx, optElemVal)
if err != nil {
ctx.ReportErrorAtID(val.ID(), "invalid map value literal %v: %v", optElemVal, err)
}
ctx.UpdateExpr(val, undoOptVal)
updatedEntries = append(updatedEntries, e)
continue
}
modified = true
if !optElemVal.HasValue() {
continue
}
ctx.UpdateExpr(val, ctx.NewLiteral(optElemVal.GetValue()))
updatedEntry := ctx.NewMapEntry(key, val, false)
updatedEntries = append(updatedEntries, updatedEntry)
}
if modified {
ctx.UpdateExpr(e, ctx.NewMap(updatedEntries))
}
}
func pruneOptionalStructFields(ctx *OptimizerContext, e ast.Expr) {
s := e.AsStruct()
fields := s.Fields()
updatedFields := []ast.EntryExpr{}
modified := false
for _, f := range fields {
field := f.AsStructField()
val := field.Value()
if !field.IsOptional() || val.Kind() != ast.LiteralKind {
updatedFields = append(updatedFields, f)
continue
}
optElemVal, ok := val.AsLiteral().(*types.Optional)
if !ok {
updatedFields = append(updatedFields, f)
continue
}
modified = true
if !optElemVal.HasValue() {
continue
}
ctx.UpdateExpr(val, ctx.NewLiteral(optElemVal.GetValue()))
updatedField := ctx.NewStructField(field.Name(), val, false)
updatedFields = append(updatedFields, updatedField)
}
if modified {
ctx.UpdateExpr(e, ctx.NewStruct(s.TypeName(), updatedFields))
}
}
// adaptLiteral converts a runtime CEL value to its equivalent literal expression.
//
// For strongly typed values, the type-provider will be used to reconstruct the fields
// which are present in the literal and their equivalent initialization values.
func adaptLiteral(ctx *OptimizerContext, val ref.Val) (ast.Expr, error) {
switch t := val.Type().(type) {
case *types.Type:
switch t {
case types.BoolType, types.BytesType, types.DoubleType, types.IntType,
types.NullType, types.StringType, types.UintType:
return ctx.NewLiteral(val), nil
case types.DurationType:
return ctx.NewCall(
overloads.TypeConvertDuration,
ctx.NewLiteral(val.ConvertToType(types.StringType)),
), nil
case types.TimestampType:
return ctx.NewCall(
overloads.TypeConvertTimestamp,
ctx.NewLiteral(val.ConvertToType(types.StringType)),
), nil
case types.OptionalType:
opt := val.(*types.Optional)
if !opt.HasValue() {
return ctx.NewCall("optional.none"), nil
}
target, err := adaptLiteral(ctx, opt.GetValue())
if err != nil {
return nil, err
}
return ctx.NewCall("optional.of", target), nil
case types.TypeType:
return ctx.NewIdent(val.(*types.Type).TypeName()), nil
case types.ListType:
l, ok := val.(traits.Lister)
if !ok {
return nil, fmt.Errorf("failed to adapt %v to literal", val)
}
elems := make([]ast.Expr, l.Size().(types.Int))
idx := 0
it := l.Iterator()
for it.HasNext() == types.True {
elemVal := it.Next()
elemExpr, err := adaptLiteral(ctx, elemVal)
if err != nil {
return nil, err
}
elems[idx] = elemExpr
idx++
}
return ctx.NewList(elems, []int32{}), nil
case types.MapType:
m, ok := val.(traits.Mapper)
if !ok {
return nil, fmt.Errorf("failed to adapt %v to literal", val)
}
entries := make([]ast.EntryExpr, m.Size().(types.Int))
idx := 0
it := m.Iterator()
for it.HasNext() == types.True {
keyVal := it.Next()
keyExpr, err := adaptLiteral(ctx, keyVal)
if err != nil {
return nil, err
}
valVal := m.Get(keyVal)
valExpr, err := adaptLiteral(ctx, valVal)
if err != nil {
return nil, err
}
entries[idx] = ctx.NewMapEntry(keyExpr, valExpr, false)
idx++
}
return ctx.NewMap(entries), nil
default:
provider := ctx.CELTypeProvider()
fields, found := provider.FindStructFieldNames(t.TypeName())
if !found {
return nil, fmt.Errorf("failed to adapt %v to literal", val)
}
tester := val.(traits.FieldTester)
indexer := val.(traits.Indexer)
fieldInits := []ast.EntryExpr{}
for _, f := range fields {
field := types.String(f)
if tester.IsSet(field) != types.True {
continue
}
fieldVal := indexer.Get(field)
fieldExpr, err := adaptLiteral(ctx, fieldVal)
if err != nil {
return nil, err
}
fieldInits = append(fieldInits, ctx.NewStructField(f, fieldExpr, false))
}
return ctx.NewStruct(t.TypeName(), fieldInits), nil
}
}
return nil, fmt.Errorf("failed to adapt %v to literal", val)
}
// constantExprMatcher matches calls, select statements, and comprehensions whose arguments
// are all constant scalar or aggregate literal values.
//
// Only comprehensions which are not nested are included as possible constant folds, and only
// if all variables referenced in the comprehension stack exist are only iteration or
// accumulation variables.
func (opt *constantFoldingOptimizer) constantExprMatcher(ctx *OptimizerContext, a *ast.AST, e ast.NavigableExpr) bool {
switch e.Kind() {
case ast.CallKind:
return constantCallMatcher(e)
case ast.SelectKind:
sel := e.AsSelect() // guaranteed to be a navigable value
return constantMatcher(sel.Operand().(ast.NavigableExpr))
case ast.IdentKind:
return opt.knownValues != nil && a.ReferenceMap()[e.ID()] != nil
case ast.ComprehensionKind:
if isNestedComprehension(e) {
return false
}
vars := map[string]bool{}
constantExprs := true
visitor := ast.NewExprVisitor(func(e ast.Expr) {
if e.Kind() == ast.ComprehensionKind {
nested := e.AsComprehension()
vars[nested.AccuVar()] = true
vars[nested.IterVar()] = true
}
if e.Kind() == ast.IdentKind && !vars[e.AsIdent()] {
constantExprs = false
}
// Late-bound function calls cannot be folded.
if e.Kind() == ast.CallKind && isLateBoundFunctionCall(ctx, a, e) {
constantExprs = false
}
})
ast.PreOrderVisit(e, visitor)
return constantExprs
default:
return false
}
}
// constantCallMatcher identifies strict and non-strict calls which can be folded.
func constantCallMatcher(e ast.NavigableExpr) bool {
call := e.AsCall()
children := e.Children()
fnName := call.FunctionName()
if fnName == operators.LogicalAnd {
for _, child := range children {
if child.Kind() == ast.LiteralKind {
return true
}
}
}
if fnName == operators.LogicalOr {
for _, child := range children {
if child.Kind() == ast.LiteralKind {
return true
}
}
}
if fnName == operators.Conditional {
cond := children[0]
if cond.Kind() == ast.LiteralKind && cond.AsLiteral().Type() == types.BoolType {
return true
}
}
if fnName == operators.In {
haystack := children[1]
if haystack.Kind() == ast.ListKind && haystack.AsList().Size() == 0 {
return true
}
needle := children[0]
if needle.Kind() == ast.LiteralKind && haystack.Kind() == ast.ListKind {
needleValue := needle.AsLiteral()
list := haystack.AsList()
for _, e := range list.Elements() {
if e.Kind() == ast.LiteralKind && e.AsLiteral().Equal(needleValue) == types.True {
return true
}
}
}
}
// convert all other calls with constant arguments
for _, child := range children {
if !constantMatcher(child) {
return false
}
}
return true
}
func isNestedComprehension(e ast.NavigableExpr) bool {
parent, found := e.Parent()
for found {
if parent.Kind() == ast.ComprehensionKind {
return true
}
parent, found = parent.Parent()
}
return false
}
func aggregateLiteralMatcher(e ast.NavigableExpr) bool {
return e.Kind() == ast.ListKind || e.Kind() == ast.MapKind || e.Kind() == ast.StructKind
}
var (
constantMatcher = ast.ConstantValueMatcher()
)
const (
defaultMaxConstantFoldIterations = 100
)
package cel
func FuzzCompile(data []byte) int {
env, err := getCELFuzzEnv()
if err != nil {
panic("impossible to create env")
}
ast, issues := env.Compile(string(data))
if issues != nil && issues.Err() != nil {
return 0
}
_, err = env.Program(ast)
if err != nil {
return 0
}
return 1
}
package cel
// Create environment for running under Address sanitizer without timing out.
func getCELFuzzEnv() (*Env, error) {
// Very dense expressions (balanced trees) can cause address sanitizer to
// timeout even though they typically fail in under a second uninstrumented.
return NewEnv(ParserRecursionLimit(60))
}
package cel
import (
"google.golang.org/protobuf/proto"
"github.com/google/cel-go/checker/decls"
exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1"
)
func FuzzEval(data []byte) int {
gen := &FuzzVariables{}
err := proto.Unmarshal(data, gen)
if err != nil {
panic("Failed to unmarshal LPM generated variables")
}
declares := make([]*exprpb.Decl, 0, len(gen.Inputs))
for k, _ := range gen.Inputs {
declares = append(declares, decls.NewVar(k, decls.String))
}
env, err := getCELFuzzEnv()
if err != nil {
panic("impossible to create env")
}
env, err = env.Extend(Declarations(declares...))
if err != nil {
panic("impossible to create env")
}
ast, issues := env.Compile(gen.Expr)
if issues != nil && issues.Err() != nil {
return 0
}
prg, err := env.Program(ast)
if err != nil {
return 0
}
//fmt.Printf("loltry %#+v\n", gen)
_, _, err = prg.Eval(gen.Inputs)
return 1
}
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package cel
import (
"github.com/google/cel-go/common/ast"
"github.com/google/cel-go/common/containers"
"github.com/google/cel-go/common/operators"
"github.com/google/cel-go/common/overloads"
"github.com/google/cel-go/common/types"
"github.com/google/cel-go/common/types/traits"
)
// InlineVariable holds a variable name to be matched and an AST representing
// the expression graph which should be used to replace it.
type InlineVariable struct {
name string
alias string
def *ast.AST
}
// Name returns the qualified variable or field selection to replace.
func (v *InlineVariable) Name() string {
return v.name
}
// Alias returns the alias to use when performing cel.bind() calls during inlining.
func (v *InlineVariable) Alias() string {
return v.alias
}
// Expr returns the inlined expression value.
func (v *InlineVariable) Expr() ast.Expr {
return v.def.Expr()
}
// Type indicates the inlined expression type.
func (v *InlineVariable) Type() *Type {
return v.def.GetType(v.def.Expr().ID())
}
// NewInlineVariable declares a variable name to be replaced by a checked expression.
func NewInlineVariable(name string, definition *Ast) *InlineVariable {
return NewInlineVariableWithAlias(name, name, definition)
}
// NewInlineVariableWithAlias declares a variable name to be replaced by a checked expression.
// If the variable occurs more than once, the provided alias will be used to replace the expressions
// where the variable name occurs.
func NewInlineVariableWithAlias(name, alias string, definition *Ast) *InlineVariable {
return &InlineVariable{name: name, alias: alias, def: definition.NativeRep()}
}
// NewInliningOptimizer creates and optimizer which replaces variables with expression definitions.
//
// If a variable occurs one time, the variable is replaced by the inline definition. If the
// variable occurs more than once, the variable occurences are replaced by a cel.bind() call.
func NewInliningOptimizer(inlineVars ...*InlineVariable) ASTOptimizer {
return &inliningOptimizer{variables: inlineVars}
}
type inliningOptimizer struct {
variables []*InlineVariable
}
func (opt *inliningOptimizer) Optimize(ctx *OptimizerContext, a *ast.AST) *ast.AST {
root := ast.NavigateAST(a)
for _, inlineVar := range opt.variables {
matches := ast.MatchDescendants(root, opt.matchVariable(inlineVar.Name()))
// Skip cases where the variable isn't in the expression graph
if len(matches) == 0 {
continue
}
// For a single match, do a direct replacement of the expression sub-graph.
if len(matches) == 1 || !isBindable(matches, inlineVar.Expr(), inlineVar.Type()) {
for _, match := range matches {
// Copy the inlined AST expr and source info.
copyExpr := ctx.CopyASTAndMetadata(inlineVar.def)
opt.inlineExpr(ctx, match, copyExpr, inlineVar.Type())
}
continue
}
// For multiple matches, find the least common ancestor (lca) and insert the
// variable as a cel.bind() macro.
var lca ast.NavigableExpr = root
lcaAncestorCount := 0
ancestors := map[int64]int{}
for _, match := range matches {
// Update the identifier matches with the provided alias.
parent, found := match, true
for found {
ancestorCount, hasAncestor := ancestors[parent.ID()]
if !hasAncestor {
ancestors[parent.ID()] = 1
parent, found = parent.Parent()
continue
}
if lcaAncestorCount < ancestorCount || (lcaAncestorCount == ancestorCount && lca.Depth() < parent.Depth()) {
lca = parent
lcaAncestorCount = ancestorCount
}
ancestors[parent.ID()] = ancestorCount + 1
parent, found = parent.Parent()
}
aliasExpr := ctx.NewIdent(inlineVar.Alias())
opt.inlineExpr(ctx, match, aliasExpr, inlineVar.Type())
}
// Copy the inlined AST expr and source info.
copyExpr := ctx.CopyASTAndMetadata(inlineVar.def)
// Update the least common ancestor by inserting a cel.bind() call to the alias.
inlined, bindMacro := ctx.NewBindMacro(lca.ID(), inlineVar.Alias(), copyExpr, lca)
opt.inlineExpr(ctx, lca, inlined, inlineVar.Type())
ctx.SetMacroCall(lca.ID(), bindMacro)
}
return a
}
// inlineExpr replaces the current expression with the inlined one, unless the location of the inlining
// happens within a presence test, e.g. has(a.b.c) -> inline alpha for a.b.c in which case an attempt is
// made to determine whether the inlined value can be presence or existence tested.
func (opt *inliningOptimizer) inlineExpr(ctx *OptimizerContext, prev ast.NavigableExpr, inlined ast.Expr, inlinedType *Type) {
switch prev.Kind() {
case ast.SelectKind:
sel := prev.AsSelect()
if !sel.IsTestOnly() {
ctx.UpdateExpr(prev, inlined)
return
}
opt.rewritePresenceExpr(ctx, prev, inlined, inlinedType)
default:
ctx.UpdateExpr(prev, inlined)
}
}
// rewritePresenceExpr converts the inlined expression, when it occurs within a has() macro, to type-safe
// expression appropriate for the inlined type, if possible.
//
// If the rewrite is not possible an error is reported at the inline expression site.
func (opt *inliningOptimizer) rewritePresenceExpr(ctx *OptimizerContext, prev, inlined ast.Expr, inlinedType *Type) {
// If the input inlined expression is not a select expression it won't work with the has()
// macro. Attempt to rewrite the presence test in terms of the typed input, otherwise error.
if inlined.Kind() == ast.SelectKind {
presenceTest, hasMacro := ctx.NewHasMacro(prev.ID(), inlined)
ctx.UpdateExpr(prev, presenceTest)
ctx.SetMacroCall(prev.ID(), hasMacro)
return
}
ctx.ClearMacroCall(prev.ID())
if inlinedType.IsAssignableType(NullType) {
ctx.UpdateExpr(prev,
ctx.NewCall(operators.NotEquals,
inlined,
ctx.NewLiteral(types.NullValue),
))
return
}
if inlinedType.HasTrait(traits.SizerType) {
ctx.UpdateExpr(prev,
ctx.NewCall(operators.NotEquals,
ctx.NewMemberCall(overloads.Size, inlined),
ctx.NewLiteral(types.IntZero),
))
return
}
ctx.ReportErrorAtID(prev.ID(), "unable to inline expression type %v into presence test", inlinedType)
}
// isBindable indicates whether the inlined type can be used within a cel.bind() if the expression
// being replaced occurs within a presence test. Value types with a size() method or field selection
// support can be bound.
//
// In future iterations, support may also be added for indexer types which can be rewritten as an `in`
// expression; however, this would imply a rewrite of the inlined expression that may not be necessary
// in most cases.
func isBindable(matches []ast.NavigableExpr, inlined ast.Expr, inlinedType *Type) bool {
if inlinedType.IsAssignableType(NullType) ||
inlinedType.HasTrait(traits.SizerType) {
return true
}
for _, m := range matches {
if m.Kind() != ast.SelectKind {
continue
}
sel := m.AsSelect()
if sel.IsTestOnly() {
return false
}
}
return true
}
// matchVariable matches simple identifiers, select expressions, and presence test expressions
// which match the (potentially) qualified variable name provided as input.
//
// Note, this function does not support inlining against select expressions which includes optional
// field selection. This may be a future refinement.
func (opt *inliningOptimizer) matchVariable(varName string) ast.ExprMatcher {
return func(e ast.NavigableExpr) bool {
if e.Kind() == ast.IdentKind && e.AsIdent() == varName {
return true
}
if e.Kind() == ast.SelectKind {
sel := e.AsSelect()
// While the `ToQualifiedName` call could take the select directly, this
// would skip presence tests from possible matches, which we would like
// to include.
qualName, found := containers.ToQualifiedName(sel.Operand())
return found && qualName+"."+sel.FieldName() == varName
}
return false
}
}
// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package cel
import (
"errors"
"fmt"
"reflect"
"google.golang.org/protobuf/proto"
"github.com/google/cel-go/common"
"github.com/google/cel-go/common/ast"
"github.com/google/cel-go/common/types"
"github.com/google/cel-go/common/types/ref"
"github.com/google/cel-go/common/types/traits"
"github.com/google/cel-go/parser"
celpb "cel.dev/expr"
exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1"
anypb "google.golang.org/protobuf/types/known/anypb"
)
// CheckedExprToAst converts a checked expression proto message to an Ast.
func CheckedExprToAst(checkedExpr *exprpb.CheckedExpr) *Ast {
checked, _ := CheckedExprToAstWithSource(checkedExpr, nil)
return checked
}
// CheckedExprToAstWithSource converts a checked expression proto message to an Ast,
// using the provided Source as the textual contents.
//
// In general the source is not necessary unless the AST has been modified between the
// `Parse` and `Check` calls as an `Ast` created from the `Parse` step will carry the source
// through future calls.
//
// Prefer CheckedExprToAst if loading expressions from storage.
func CheckedExprToAstWithSource(checkedExpr *exprpb.CheckedExpr, src Source) (*Ast, error) {
checked, err := ast.ToAST(checkedExpr)
if err != nil {
return nil, err
}
return &Ast{source: src, impl: checked}, nil
}
// AstToCheckedExpr converts an Ast to an protobuf CheckedExpr value.
//
// If the Ast.IsChecked() returns false, this conversion method will return an error.
func AstToCheckedExpr(a *Ast) (*exprpb.CheckedExpr, error) {
if !a.IsChecked() {
return nil, fmt.Errorf("cannot convert unchecked ast")
}
return ast.ToProto(a.NativeRep())
}
// ParsedExprToAst converts a parsed expression proto message to an Ast.
func ParsedExprToAst(parsedExpr *exprpb.ParsedExpr) *Ast {
return ParsedExprToAstWithSource(parsedExpr, nil)
}
// ParsedExprToAstWithSource converts a parsed expression proto message to an Ast,
// using the provided Source as the textual contents.
//
// In general you only need this if you need to recheck a previously checked
// expression, or if you need to separately check a subset of an expression.
//
// Prefer ParsedExprToAst if loading expressions from storage.
func ParsedExprToAstWithSource(parsedExpr *exprpb.ParsedExpr, src Source) *Ast {
info, _ := ast.ProtoToSourceInfo(parsedExpr.GetSourceInfo())
if src == nil {
src = common.NewInfoSource(parsedExpr.GetSourceInfo())
}
e, _ := ast.ProtoToExpr(parsedExpr.GetExpr())
return &Ast{source: src, impl: ast.NewAST(e, info)}
}
// AstToParsedExpr converts an Ast to an protobuf ParsedExpr value.
func AstToParsedExpr(a *Ast) (*exprpb.ParsedExpr, error) {
return &exprpb.ParsedExpr{
Expr: a.Expr(),
SourceInfo: a.SourceInfo(),
}, nil
}
// AstToString converts an Ast back to a string if possible.
//
// Note, the conversion may not be an exact replica of the original expression, but will produce
// a string that is semantically equivalent and whose textual representation is stable.
func AstToString(a *Ast) (string, error) {
return ExprToString(a.NativeRep().Expr(), a.NativeRep().SourceInfo())
}
// ExprToString converts an AST Expr node back to a string using macro call tracking metadata from
// source info if any macros are encountered within the expression.
func ExprToString(e ast.Expr, info *ast.SourceInfo) (string, error) {
return parser.Unparse(e, info)
}
// RefValueToValue converts between ref.Val and google.api.expr.v1alpha1.Value.
// The result Value is the serialized proto form. The ref.Val must not be error or unknown.
func RefValueToValue(res ref.Val) (*exprpb.Value, error) {
return ValueAsAlphaProto(res)
}
// ValueAsAlphaProto converts between ref.Val and google.api.expr.v1alpha1.Value.
// The result Value is the serialized proto form. The ref.Val must not be error or unknown.
func ValueAsAlphaProto(res ref.Val) (*exprpb.Value, error) {
canonical, err := ValueAsProto(res)
if err != nil {
return nil, err
}
alpha := &exprpb.Value{}
err = convertProto(canonical, alpha)
return alpha, err
}
// RefValToExprValue converts between ref.Val and google.api.expr.v1alpha1.ExprValue.
// The result ExprValue is the serialized proto form.
func RefValToExprValue(res ref.Val) (*exprpb.ExprValue, error) {
return ExprValueAsAlphaProto(res)
}
// ExprValueAsAlphaProto converts between ref.Val and google.api.expr.v1alpha1.ExprValue.
// The result ExprValue is the serialized proto form.
func ExprValueAsAlphaProto(res ref.Val) (*exprpb.ExprValue, error) {
canonical, err := ExprValueAsProto(res)
if err != nil {
return nil, err
}
alpha := &exprpb.ExprValue{}
err = convertProto(canonical, alpha)
return alpha, err
}
// ExprValueAsProto converts between ref.Val and cel.expr.ExprValue.
// The result ExprValue is the serialized proto form.
func ExprValueAsProto(res ref.Val) (*celpb.ExprValue, error) {
switch res := res.(type) {
case *types.Unknown:
return &celpb.ExprValue{
Kind: &celpb.ExprValue_Unknown{
Unknown: &celpb.UnknownSet{
Exprs: res.IDs(),
},
}}, nil
case *types.Err:
return &celpb.ExprValue{
Kind: &celpb.ExprValue_Error{
Error: &celpb.ErrorSet{
// Keeping the error code as UNKNOWN since there's no error codes associated with
// Cel-Go runtime errors.
Errors: []*celpb.Status{{Code: 2, Message: res.Error()}},
},
},
}, nil
default:
val, err := ValueAsProto(res)
if err != nil {
return nil, err
}
return &celpb.ExprValue{
Kind: &celpb.ExprValue_Value{Value: val}}, nil
}
}
// ValueAsProto converts between ref.Val and cel.expr.Value.
// The result Value is the serialized proto form. The ref.Val must not be error or unknown.
func ValueAsProto(res ref.Val) (*celpb.Value, error) {
switch res.Type() {
case types.BoolType:
return &celpb.Value{
Kind: &celpb.Value_BoolValue{BoolValue: res.Value().(bool)}}, nil
case types.BytesType:
return &celpb.Value{
Kind: &celpb.Value_BytesValue{BytesValue: res.Value().([]byte)}}, nil
case types.DoubleType:
return &celpb.Value{
Kind: &celpb.Value_DoubleValue{DoubleValue: res.Value().(float64)}}, nil
case types.IntType:
return &celpb.Value{
Kind: &celpb.Value_Int64Value{Int64Value: res.Value().(int64)}}, nil
case types.ListType:
l := res.(traits.Lister)
sz := l.Size().(types.Int)
elts := make([]*celpb.Value, 0, int64(sz))
for i := types.Int(0); i < sz; i++ {
v, err := ValueAsProto(l.Get(i))
if err != nil {
return nil, err
}
elts = append(elts, v)
}
return &celpb.Value{
Kind: &celpb.Value_ListValue{
ListValue: &celpb.ListValue{Values: elts}}}, nil
case types.MapType:
mapper := res.(traits.Mapper)
sz := mapper.Size().(types.Int)
entries := make([]*celpb.MapValue_Entry, 0, int64(sz))
for it := mapper.Iterator(); it.HasNext().(types.Bool); {
k := it.Next()
v := mapper.Get(k)
kv, err := ValueAsProto(k)
if err != nil {
return nil, err
}
vv, err := ValueAsProto(v)
if err != nil {
return nil, err
}
entries = append(entries, &celpb.MapValue_Entry{Key: kv, Value: vv})
}
return &celpb.Value{
Kind: &celpb.Value_MapValue{
MapValue: &celpb.MapValue{Entries: entries}}}, nil
case types.NullType:
return &celpb.Value{
Kind: &celpb.Value_NullValue{}}, nil
case types.StringType:
return &celpb.Value{
Kind: &celpb.Value_StringValue{StringValue: res.Value().(string)}}, nil
case types.TypeType:
typeName := res.(ref.Type).TypeName()
return &celpb.Value{Kind: &celpb.Value_TypeValue{TypeValue: typeName}}, nil
case types.UintType:
return &celpb.Value{
Kind: &celpb.Value_Uint64Value{Uint64Value: res.Value().(uint64)}}, nil
default:
any, err := res.ConvertToNative(anyPbType)
if err != nil {
return nil, err
}
return &celpb.Value{
Kind: &celpb.Value_ObjectValue{ObjectValue: any.(*anypb.Any)}}, nil
}
}
var (
typeNameToTypeValue = map[string]ref.Val{
"bool": types.BoolType,
"bytes": types.BytesType,
"double": types.DoubleType,
"null_type": types.NullType,
"int": types.IntType,
"list": types.ListType,
"map": types.MapType,
"string": types.StringType,
"type": types.TypeType,
"uint": types.UintType,
}
anyPbType = reflect.TypeOf(&anypb.Any{})
)
// ValueToRefValue converts between google.api.expr.v1alpha1.Value and ref.Val.
func ValueToRefValue(adapter types.Adapter, v *exprpb.Value) (ref.Val, error) {
return AlphaProtoAsValue(adapter, v)
}
// AlphaProtoAsValue converts between google.api.expr.v1alpha1.Value and ref.Val.
func AlphaProtoAsValue(adapter types.Adapter, v *exprpb.Value) (ref.Val, error) {
canonical := &celpb.Value{}
if err := convertProto(v, canonical); err != nil {
return nil, err
}
return ProtoAsValue(adapter, canonical)
}
// ProtoAsValue converts between cel.expr.Value and ref.Val.
func ProtoAsValue(adapter types.Adapter, v *celpb.Value) (ref.Val, error) {
switch v.Kind.(type) {
case *celpb.Value_NullValue:
return types.NullValue, nil
case *celpb.Value_BoolValue:
return types.Bool(v.GetBoolValue()), nil
case *celpb.Value_Int64Value:
return types.Int(v.GetInt64Value()), nil
case *celpb.Value_Uint64Value:
return types.Uint(v.GetUint64Value()), nil
case *celpb.Value_DoubleValue:
return types.Double(v.GetDoubleValue()), nil
case *celpb.Value_StringValue:
return types.String(v.GetStringValue()), nil
case *celpb.Value_BytesValue:
return types.Bytes(v.GetBytesValue()), nil
case *celpb.Value_ObjectValue:
any := v.GetObjectValue()
msg, err := anypb.UnmarshalNew(any, proto.UnmarshalOptions{DiscardUnknown: true})
if err != nil {
return nil, err
}
return adapter.NativeToValue(msg), nil
case *celpb.Value_MapValue:
m := v.GetMapValue()
entries := make(map[ref.Val]ref.Val)
for _, entry := range m.Entries {
key, err := ProtoAsValue(adapter, entry.Key)
if err != nil {
return nil, err
}
pb, err := ProtoAsValue(adapter, entry.Value)
if err != nil {
return nil, err
}
entries[key] = pb
}
return adapter.NativeToValue(entries), nil
case *celpb.Value_ListValue:
l := v.GetListValue()
elts := make([]ref.Val, len(l.Values))
for i, e := range l.Values {
rv, err := ProtoAsValue(adapter, e)
if err != nil {
return nil, err
}
elts[i] = rv
}
return adapter.NativeToValue(elts), nil
case *celpb.Value_TypeValue:
typeName := v.GetTypeValue()
tv, ok := typeNameToTypeValue[typeName]
if ok {
return tv, nil
}
return types.NewObjectTypeValue(typeName), nil
}
return nil, errors.New("unknown value")
}
func convertProto(src, dst proto.Message) error {
pb, err := proto.Marshal(src)
if err != nil {
return err
}
err = proto.Unmarshal(pb, dst)
return err
}
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package cel
import (
"fmt"
"math"
"github.com/google/cel-go/common"
"github.com/google/cel-go/common/ast"
"github.com/google/cel-go/common/decls"
"github.com/google/cel-go/common/env"
"github.com/google/cel-go/common/operators"
"github.com/google/cel-go/common/overloads"
"github.com/google/cel-go/common/stdlib"
"github.com/google/cel-go/common/types"
"github.com/google/cel-go/common/types/ref"
"github.com/google/cel-go/common/types/traits"
"github.com/google/cel-go/interpreter"
"github.com/google/cel-go/parser"
)
const (
optMapMacro = "optMap"
optFlatMapMacro = "optFlatMap"
hasValueFunc = "hasValue"
unwrapOptFunc = "unwrapOpt"
optionalNoneFunc = "optional.none"
optionalOfFunc = "optional.of"
optionalOfNonZeroValueFunc = "optional.ofNonZeroValue"
optionalUnwrapFunc = "optional.unwrap"
valueFunc = "value"
unusedIterVar = "#unused"
)
// Library provides a collection of EnvOption and ProgramOption values used to configure a CEL
// environment for a particular use case or with a related set of functionality.
//
// Note, the ProgramOption values provided by a library are expected to be static and not vary
// between calls to Env.Program(). If there is a need for such dynamic configuration, prefer to
// configure these options outside the Library and within the Env.Program() call directly.
type Library interface {
// CompileOptions returns a collection of functional options for configuring the Parse / Check
// environment.
CompileOptions() []EnvOption
// ProgramOptions returns a collection of functional options which should be included in every
// Program generated from the Env.Program() call.
ProgramOptions() []ProgramOption
}
// SingletonLibrary refines the Library interface to ensure that libraries in this format are only
// configured once within the environment.
type SingletonLibrary interface {
Library
// LibraryName provides a namespaced name which is used to check whether the library has already
// been configured in the environment.
LibraryName() string
}
// LibraryAliaser generates a simple named alias for the library, for use during environment serialization.
type LibraryAliaser interface {
LibraryAlias() string
}
// LibrarySubsetter provides the subset description associated with the library, nil if not subset.
type LibrarySubsetter interface {
LibrarySubset() *env.LibrarySubset
}
// LibraryVersioner provides a version number for the library.
//
// If not implemented, the library version will be flagged as 'latest' during environment serialization.
type LibraryVersioner interface {
LibraryVersion() uint32
}
// Lib creates an EnvOption out of a Library, allowing libraries to be provided as functional args,
// and to be linked to each other.
func Lib(l Library) EnvOption {
singleton, isSingleton := l.(SingletonLibrary)
return func(e *Env) (*Env, error) {
if isSingleton {
if e.HasLibrary(singleton.LibraryName()) {
return e, nil
}
e.libraries[singleton.LibraryName()] = singleton
}
var err error
for _, opt := range l.CompileOptions() {
e, err = opt(e)
if err != nil {
return nil, err
}
}
e.progOpts = append(e.progOpts, l.ProgramOptions()...)
return e, nil
}
}
// StdLibOption specifies a functional option for configuring the standard CEL library.
type StdLibOption func(*stdLibrary) *stdLibrary
// StdLibSubset configures the standard library to use a subset of its functions and macros.
//
// Since the StdLib is a singleton library, only the first instance of the StdLib() environment options
// will be configured on the environment which means only the StdLibSubset() initially configured with
// the library will be used.
func StdLibSubset(subset *env.LibrarySubset) StdLibOption {
return func(lib *stdLibrary) *stdLibrary {
lib.subset = subset
return lib
}
}
// StdLib returns an EnvOption for the standard library of CEL functions and macros.
func StdLib(opts ...StdLibOption) EnvOption {
lib := &stdLibrary{}
for _, o := range opts {
lib = o(lib)
}
return Lib(lib)
}
// stdLibrary implements the Library interface and provides functional options for the core CEL
// features documented in the specification.
type stdLibrary struct {
subset *env.LibrarySubset
}
// LibraryName implements the SingletonLibrary interface method.
func (*stdLibrary) LibraryName() string {
return "cel.lib.std"
}
// LibraryAlias returns the simple name of the library.
func (*stdLibrary) LibraryAlias() string {
return "stdlib"
}
// LibrarySubset returns the env.LibrarySubset definition associated with the CEL Library.
func (lib *stdLibrary) LibrarySubset() *env.LibrarySubset {
return lib.subset
}
// CompileOptions returns options for the standard CEL function declarations and macros.
func (lib *stdLibrary) CompileOptions() []EnvOption {
funcs := stdlib.Functions()
macros := StandardMacros
if lib.subset != nil {
subMacros := []Macro{}
for _, m := range macros {
if lib.subset.SubsetMacro(m.Function()) {
subMacros = append(subMacros, m)
}
}
macros = subMacros
subFuncs := []*decls.FunctionDecl{}
for _, fn := range funcs {
if f, include := lib.subset.SubsetFunction(fn); include {
subFuncs = append(subFuncs, f)
}
}
funcs = subFuncs
}
return []EnvOption{
func(e *Env) (*Env, error) {
var err error
if err = lib.subset.Validate(); err != nil {
return nil, err
}
e.variables = append(e.variables, stdlib.Types()...)
for _, fn := range funcs {
existing, found := e.functions[fn.Name()]
if found {
fn, err = existing.Merge(fn)
if err != nil {
return nil, err
}
}
e.functions[fn.Name()] = fn
}
return e, nil
},
Macros(macros...),
}
}
// ProgramOptions returns function implementations for the standard CEL functions.
func (*stdLibrary) ProgramOptions() []ProgramOption {
return []ProgramOption{}
}
// OptionalTypes enable support for optional syntax and types in CEL.
//
// The optional value type makes it possible to express whether variables have
// been provided, whether a result has been computed, and in the future whether
// an object field path, map key value, or list index has a value.
//
// # Syntax Changes
//
// OptionalTypes are unlike other CEL extensions because they modify the CEL
// syntax itself, notably through the use of a `?` preceding a field name or
// index value.
//
// ## Field Selection
//
// The optional syntax in field selection is denoted as `obj.?field`. In other
// words, if a field is set, return `optional.of(obj.field)“, else
// `optional.none()`. The optional field selection is viral in the sense that
// after the first optional selection all subsequent selections or indices
// are treated as optional, i.e. the following expressions are equivalent:
//
// obj.?field.subfield
// obj.?field.?subfield
//
// ## Indexing
//
// Similar to field selection, the optional syntax can be used in index
// expressions on maps and lists:
//
// list[?0]
// map[?key]
//
// ## Optional Field Setting
//
// When creating map or message literals, if a field may be optionally set
// based on its presence, then placing a `?` before the field name or key
// will ensure the type on the right-hand side must be optional(T) where T
// is the type of the field or key-value.
//
// The following returns a map with the key expression set only if the
// subfield is present, otherwise an empty map is created:
//
// {?key: obj.?field.subfield}
//
// ## Optional Element Setting
//
// When creating list literals, an element in the list may be optionally added
// when the element expression is preceded by a `?`:
//
// [a, ?b, ?c] // return a list with either [a], [a, b], [a, b, c], or [a, c]
//
// # Optional.Of
//
// Create an optional(T) value of a given value with type T.
//
// optional.of(10)
//
// # Optional.OfNonZeroValue
//
// Create an optional(T) value of a given value with type T if it is not a
// zero-value. A zero-value the default empty value for any given CEL type,
// including empty protobuf message types. If the value is empty, the result
// of this call will be optional.none().
//
// optional.ofNonZeroValue([1, 2, 3]) // optional(list(int))
// optional.ofNonZeroValue([]) // optional.none()
// optional.ofNonZeroValue(0) // optional.none()
// optional.ofNonZeroValue("") // optional.none()
//
// # Optional.None
//
// Create an empty optional value.
//
// # HasValue
//
// Determine whether the optional contains a value.
//
// optional.of(b'hello').hasValue() // true
// optional.ofNonZeroValue({}).hasValue() // false
//
// # Value
//
// Get the value contained by the optional. If the optional does not have a
// value, the result will be a CEL error.
//
// optional.of(b'hello').value() // b'hello'
// optional.ofNonZeroValue({}).value() // error
//
// # Or
//
// If the value on the left-hand side is optional.none(), the optional value
// on the right hand side is returned. If the value on the left-hand set is
// valued, then it is returned. This operation is short-circuiting and will
// only evaluate as many links in the `or` chain as are needed to return a
// non-empty optional value.
//
// obj.?field.or(m[?key])
// l[?index].or(obj.?field.subfield).or(obj.?other)
//
// # OrValue
//
// Either return the value contained within the optional on the left-hand side
// or return the alternative value on the right hand side.
//
// m[?key].orValue("none")
//
// # OptMap
//
// Apply a transformation to the optional's underlying value if it is not empty
// and return an optional typed result based on the transformation. The
// transformation expression type must return a type T which is wrapped into
// an optional.
//
// msg.?elements.optMap(e, e.size()).orValue(0)
//
// # OptFlatMap
//
// Introduced in version: 1
//
// Apply a transformation to the optional's underlying value if it is not empty
// and return the result. The transform expression must return an optional(T)
// rather than type T. This can be useful when dealing with zero values and
// conditionally generating an empty or non-empty result in ways which cannot
// be expressed with `optMap`.
//
// msg.?elements.optFlatMap(e, e[?0]) // return the first element if present.
//
// # First
//
// Introduced in version: 2
//
// Returns an optional with the first value from the right hand list, or
// optional.None.
//
// [1, 2, 3].first().value() == 1
//
// # Last
//
// Introduced in version: 2
//
// Returns an optional with the last value from the right hand list, or
// optional.None.
//
// [1, 2, 3].last().value() == 3
//
// This is syntactic sugar for msg.elements[msg.elements.size()-1].
//
// # Unwrap / UnwrapOpt
//
// Introduced in version: 2
//
// Returns a list of all the values that are not none in the input list of optional values.
// Can be used as optional.unwrap(List[T]) or with postfix notation: List[T].unwrapOpt()
//
// optional.unwrap([optional.of(42), optional.none()]) == [42]
// [optional.of(42), optional.none()].unwrapOpt() == [42]
func OptionalTypes(opts ...OptionalTypesOption) EnvOption {
lib := &optionalLib{version: math.MaxUint32}
for _, opt := range opts {
lib = opt(lib)
}
return Lib(lib)
}
type optionalLib struct {
version uint32
}
// OptionalTypesOption is a functional interface for configuring the strings library.
type OptionalTypesOption func(*optionalLib) *optionalLib
// OptionalTypesVersion configures the version of the optional type library.
//
// The version limits which functions are available. Only functions introduced
// below or equal to the given version included in the library. If this option
// is not set, all functions are available.
//
// See the library documentation to determine which version a function was introduced.
// If the documentation does not state which version a function was introduced, it can
// be assumed to be introduced at version 0, when the library was first created.
func OptionalTypesVersion(version uint32) OptionalTypesOption {
return func(lib *optionalLib) *optionalLib {
lib.version = version
return lib
}
}
// LibraryName implements the SingletonLibrary interface method.
func (*optionalLib) LibraryName() string {
return "cel.lib.optional"
}
// LibraryAlias returns the simple name of the library.
func (*optionalLib) LibraryAlias() string {
return "optional"
}
// LibraryVersion returns the version of the library.
func (lib *optionalLib) LibraryVersion() uint32 {
return lib.version
}
// CompileOptions implements the Library interface method.
func (lib *optionalLib) CompileOptions() []EnvOption {
paramTypeK := TypeParamType("K")
paramTypeV := TypeParamType("V")
optionalTypeV := OptionalType(paramTypeV)
listTypeV := ListType(paramTypeV)
mapTypeKV := MapType(paramTypeK, paramTypeV)
listOptionalTypeV := ListType(optionalTypeV)
opts := []EnvOption{
// Enable the optional syntax in the parser.
enableOptionalSyntax(),
// Introduce the optional type.
Types(types.OptionalType),
// Configure the optMap and optFlatMap macros.
Macros(ReceiverMacro(optMapMacro, 2, optMap,
MacroDocs(`perform computation on the value if present and return the result as an optional`),
MacroExamples(
common.MultilineDescription(
`// sub with the prefix 'dev.cel' or optional.none()`,
`request.auth.tokens.?sub.optMap(id, 'dev.cel.' + id)`),
`optional.none().optMap(i, i * 2) // optional.none()`))),
// Global and member functions for working with optional values.
Function(optionalOfFunc,
FunctionDocs(`create a new optional_type(T) with a value where any value is considered valid`),
Overload("optional_of", []*Type{paramTypeV}, optionalTypeV,
OverloadExamples(`optional.of(1) // optional(1)`),
UnaryBinding(func(value ref.Val) ref.Val {
return types.OptionalOf(value)
}))),
Function(optionalOfNonZeroValueFunc,
FunctionDocs(`create a new optional_type(T) with a value, if the value is not a zero or empty value`),
Overload("optional_ofNonZeroValue", []*Type{paramTypeV}, optionalTypeV,
OverloadExamples(
`optional.ofNonZeroValue(null) // optional.none()`,
`optional.ofNonZeroValue("") // optional.none()`,
`optional.ofNonZeroValue("hello") // optional.of('hello')`),
UnaryBinding(func(value ref.Val) ref.Val {
v, isZeroer := value.(traits.Zeroer)
if !isZeroer || !v.IsZeroValue() {
return types.OptionalOf(value)
}
return types.OptionalNone
}))),
Function(optionalNoneFunc,
FunctionDocs(`singleton value representing an optional without a value`),
Overload("optional_none", []*Type{}, optionalTypeV,
OverloadExamples(`optional.none()`),
FunctionBinding(func(values ...ref.Val) ref.Val {
return types.OptionalNone
}))),
Function(valueFunc,
FunctionDocs(`obtain the value contained by the optional, error if optional.none()`),
MemberOverload("optional_value", []*Type{optionalTypeV}, paramTypeV,
OverloadExamples(
`optional.of(1).value() // 1`,
`optional.none().value() // error`),
UnaryBinding(func(value ref.Val) ref.Val {
opt := value.(*types.Optional)
return opt.GetValue()
}))),
Function(hasValueFunc,
FunctionDocs(`determine whether the optional contains a value`),
MemberOverload("optional_hasValue", []*Type{optionalTypeV}, BoolType,
OverloadExamples(`optional.of({1: 2}).hasValue() // true`),
UnaryBinding(func(value ref.Val) ref.Val {
opt := value.(*types.Optional)
return types.Bool(opt.HasValue())
}))),
// Implementation of 'or' and 'orValue' are special-cased to support short-circuiting in the
// evaluation chain.
Function("or",
FunctionDocs(`chain optional expressions together, picking the first valued optional expression`),
MemberOverload("optional_or_optional", []*Type{optionalTypeV, optionalTypeV}, optionalTypeV,
OverloadExamples(
`optional.none().or(optional.of(1)) // optional.of(1)`,
common.MultilineDescription(
`// either a value from the first list, a value from the second, or optional.none()`,
`[1, 2, 3][?x].or([3, 4, 5][?y])`)))),
Function("orValue",
FunctionDocs(`chain optional expressions together picking the first valued optional or the default value`),
MemberOverload("optional_orValue_value", []*Type{optionalTypeV, paramTypeV}, paramTypeV,
OverloadExamples(
common.MultilineDescription(
`// pick the value for the given key if the key exists, otherwise return 'you'`,
`{'hello': 'world', 'goodbye': 'cruel world'}[?greeting].orValue('you')`)))),
// OptSelect is handled specially by the type-checker, so the receiver's field type is used to determine the
// optput type.
Function(operators.OptSelect,
FunctionDocs(`if the field is present create an optional of the field value, otherwise return optional.none()`),
Overload("select_optional_field", []*Type{DynType, StringType}, optionalTypeV,
OverloadExamples(
`msg.?field // optional.of(field) if non-empty, otherwise optional.none()`,
`msg.?field.?nested_field // optional.of(nested_field) if both field and nested_field are non-empty.`))),
// OptIndex is handled mostly like any other indexing operation on a list or map, so the type-checker can use
// these signatures to determine type-agreement without any special handling.
Function(operators.OptIndex,
FunctionDocs(`if the index is present create an optional of the field value, otherwise return optional.none()`),
Overload("list_optindex_optional_int", []*Type{listTypeV, IntType}, optionalTypeV,
OverloadExamples(`[1, 2, 3][?x] // element value if x is in the list size, else optional.none()`)),
Overload("optional_list_optindex_optional_int", []*Type{OptionalType(listTypeV), IntType}, optionalTypeV),
Overload("map_optindex_optional_value", []*Type{mapTypeKV, paramTypeK}, optionalTypeV,
OverloadExamples(
`map_value[?key] // value at the key if present, else optional.none()`,
common.MultilineDescription(
`// map key-value if index is a valid map key, else optional.none()`,
`{0: 2, 2: 4, 6: 8}[?index]`))),
Overload("optional_map_optindex_optional_value", []*Type{OptionalType(mapTypeKV), paramTypeK}, optionalTypeV)),
// Index overloads to accommodate using an optional value as the operand.
Function(operators.Index,
Overload("optional_list_index_int", []*Type{OptionalType(listTypeV), IntType}, optionalTypeV),
Overload("optional_map_index_value", []*Type{OptionalType(mapTypeKV), paramTypeK}, optionalTypeV)),
}
if lib.version >= 1 {
opts = append(opts, Macros(ReceiverMacro(optFlatMapMacro, 2, optFlatMap,
MacroDocs(`perform computation on the value if present and produce an optional value within the computation`),
MacroExamples(
common.MultilineDescription(
`// m = {'key': {}}`,
`m.?key.optFlatMap(k, k.?subkey) // optional.none()`),
common.MultilineDescription(
`// m = {'key': {'subkey': 'value'}}`,
`m.?key.optFlatMap(k, k.?subkey) // optional.of('value')`),
))))
}
if lib.version >= 2 {
opts = append(opts, Function("last",
FunctionDocs(`return the last value in a list if present, otherwise optional.none()`),
MemberOverload("list_last", []*Type{listTypeV}, optionalTypeV,
OverloadExamples(
`[].last() // optional.none()`,
`[1, 2, 3].last() ? optional.of(3)`),
UnaryBinding(func(v ref.Val) ref.Val {
list := v.(traits.Lister)
sz := list.Size().(types.Int)
if sz == types.IntZero {
return types.OptionalNone
}
return types.OptionalOf(list.Get(types.Int(sz - 1)))
}),
),
))
opts = append(opts, Function("first",
FunctionDocs(`return the first value in a list if present, otherwise optional.none()`),
MemberOverload("list_first", []*Type{listTypeV}, optionalTypeV,
OverloadExamples(
`[].first() // optional.none()`,
`[1, 2, 3].first() ? optional.of(1)`),
UnaryBinding(func(v ref.Val) ref.Val {
list := v.(traits.Lister)
sz := list.Size().(types.Int)
if sz == types.IntZero {
return types.OptionalNone
}
return types.OptionalOf(list.Get(types.Int(0)))
}),
),
))
opts = append(opts, Function(optionalUnwrapFunc,
FunctionDocs(`convert a list of optional values to a list containing only value which are not optional.none()`),
Overload("optional_unwrap", []*Type{listOptionalTypeV}, listTypeV,
OverloadExamples(`optional.unwrap([optional.of(1), optional.none()]) // [1]`),
UnaryBinding(optUnwrap))))
opts = append(opts, Function(unwrapOptFunc,
FunctionDocs(`convert a list of optional values to a list containing only value which are not optional.none()`),
MemberOverload("optional_unwrapOpt", []*Type{listOptionalTypeV}, listTypeV,
OverloadExamples(`[optional.of(1), optional.none()].unwrapOpt() // [1]`),
UnaryBinding(optUnwrap))))
}
return opts
}
// ProgramOptions implements the Library interface method.
func (lib *optionalLib) ProgramOptions() []ProgramOption {
return []ProgramOption{
CustomDecorator(decorateOptionalOr),
}
}
// Version returns the current version of the library.
func (lib *optionalLib) Version() uint32 {
return lib.version
}
func optMap(meh MacroExprFactory, target ast.Expr, args []ast.Expr) (ast.Expr, *Error) {
varIdent := args[0]
varName := ""
switch varIdent.Kind() {
case ast.IdentKind:
varName = varIdent.AsIdent()
default:
return nil, meh.NewError(varIdent.ID(), "optMap() variable name must be a simple identifier")
}
mapExpr := args[1]
return meh.NewCall(
operators.Conditional,
meh.NewMemberCall(hasValueFunc, target),
meh.NewCall(optionalOfFunc,
meh.NewComprehension(
meh.NewList(),
unusedIterVar,
varName,
meh.NewMemberCall(valueFunc, meh.Copy(target)),
meh.NewLiteral(types.False),
meh.NewIdent(varName),
mapExpr,
),
),
meh.NewCall(optionalNoneFunc),
), nil
}
func optFlatMap(meh MacroExprFactory, target ast.Expr, args []ast.Expr) (ast.Expr, *Error) {
varIdent := args[0]
varName := ""
switch varIdent.Kind() {
case ast.IdentKind:
varName = varIdent.AsIdent()
default:
return nil, meh.NewError(varIdent.ID(), "optFlatMap() variable name must be a simple identifier")
}
mapExpr := args[1]
return meh.NewCall(
operators.Conditional,
meh.NewMemberCall(hasValueFunc, target),
meh.NewComprehension(
meh.NewList(),
unusedIterVar,
varName,
meh.NewMemberCall(valueFunc, meh.Copy(target)),
meh.NewLiteral(types.False),
meh.NewIdent(varName),
mapExpr,
),
meh.NewCall(optionalNoneFunc),
), nil
}
func optUnwrap(value ref.Val) ref.Val {
list := value.(traits.Lister)
var unwrappedList []ref.Val
iter := list.Iterator()
for iter.HasNext() == types.True {
val := iter.Next()
opt, isOpt := val.(*types.Optional)
if !isOpt {
return types.WrapErr(fmt.Errorf("value %v is not optional", val))
}
if opt.HasValue() {
unwrappedList = append(unwrappedList, opt.GetValue())
}
}
return types.DefaultTypeAdapter.NativeToValue(unwrappedList)
}
func enableOptionalSyntax() EnvOption {
return func(e *Env) (*Env, error) {
e.prsrOpts = append(e.prsrOpts, parser.EnableOptionalSyntax(true))
return e, nil
}
}
// EnableErrorOnBadPresenceTest enables error generation when a presence test or optional field
// selection is performed on a primitive type.
func EnableErrorOnBadPresenceTest(value bool) EnvOption {
return features(featureEnableErrorOnBadPresenceTest, value)
}
func decorateOptionalOr(i interpreter.Interpretable) (interpreter.Interpretable, error) {
call, ok := i.(interpreter.InterpretableCall)
if !ok {
return i, nil
}
args := call.Args()
if len(args) != 2 {
return i, nil
}
switch call.Function() {
case "or":
if call.OverloadID() != "" && call.OverloadID() != "optional_or_optional" {
return i, nil
}
return &evalOptionalOr{
id: call.ID(),
lhs: args[0],
rhs: args[1],
}, nil
case "orValue":
if call.OverloadID() != "" && call.OverloadID() != "optional_orValue_value" {
return i, nil
}
return &evalOptionalOrValue{
id: call.ID(),
lhs: args[0],
rhs: args[1],
}, nil
default:
return i, nil
}
}
// evalOptionalOr selects between two optional values, either the first if it has a value, or
// the second optional expression is evaluated and returned.
type evalOptionalOr struct {
id int64
lhs interpreter.Interpretable
rhs interpreter.Interpretable
}
// ID implements the Interpretable interface method.
func (opt *evalOptionalOr) ID() int64 {
return opt.id
}
// Eval evaluates the left-hand side optional to determine whether it contains a value, else
// proceeds with the right-hand side evaluation.
func (opt *evalOptionalOr) Eval(ctx interpreter.Activation) ref.Val {
// short-circuit lhs.
optLHS := opt.lhs.Eval(ctx)
optVal, ok := optLHS.(*types.Optional)
if !ok {
return optLHS
}
if optVal.HasValue() {
return optVal
}
return opt.rhs.Eval(ctx)
}
// evalOptionalOrValue selects between an optional or a concrete value. If the optional has a value,
// its value is returned, otherwise the alternative value expression is evaluated and returned.
type evalOptionalOrValue struct {
id int64
lhs interpreter.Interpretable
rhs interpreter.Interpretable
}
// ID implements the Interpretable interface method.
func (opt *evalOptionalOrValue) ID() int64 {
return opt.id
}
// Eval evaluates the left-hand side optional to determine whether it contains a value, else
// proceeds with the right-hand side evaluation.
func (opt *evalOptionalOrValue) Eval(ctx interpreter.Activation) ref.Val {
// short-circuit lhs.
optLHS := opt.lhs.Eval(ctx)
optVal, ok := optLHS.(*types.Optional)
if !ok {
return optLHS
}
if optVal.HasValue() {
return optVal.GetValue()
}
return opt.rhs.Eval(ctx)
}
type timeLegacyLibrary struct{}
func (timeLegacyLibrary) CompileOptions() []EnvOption {
return timeOverloadDeclarations
}
func (timeLegacyLibrary) ProgramOptions() []ProgramOption {
return []ProgramOption{}
}
// Declarations and functions which enable using UTC on time.Time inputs when the timezone is unspecified
// in the CEL expression.
var (
timeOverloadDeclarations = []EnvOption{
Function(overloads.TimeGetFullYear,
MemberOverload(overloads.TimestampToYear, []*Type{TimestampType}, IntType,
UnaryBinding(func(ts ref.Val) ref.Val {
t := ts.(types.Timestamp)
return t.Receive(overloads.TimeGetFullYear, overloads.TimestampToYear, []ref.Val{})
}),
),
),
Function(overloads.TimeGetMonth,
MemberOverload(overloads.TimestampToMonth, []*Type{TimestampType}, IntType,
UnaryBinding(func(ts ref.Val) ref.Val {
t := ts.(types.Timestamp)
return t.Receive(overloads.TimeGetMonth, overloads.TimestampToMonth, []ref.Val{})
}),
),
),
Function(overloads.TimeGetDayOfYear,
MemberOverload(overloads.TimestampToDayOfYear, []*Type{TimestampType}, IntType,
UnaryBinding(func(ts ref.Val) ref.Val {
t := ts.(types.Timestamp)
return t.Receive(overloads.TimeGetDayOfYear, overloads.TimestampToDayOfYear, []ref.Val{})
}),
),
),
Function(overloads.TimeGetDayOfMonth,
MemberOverload(overloads.TimestampToDayOfMonthZeroBased, []*Type{TimestampType}, IntType,
UnaryBinding(func(ts ref.Val) ref.Val {
t := ts.(types.Timestamp)
return t.Receive(overloads.TimeGetDayOfMonth, overloads.TimestampToDayOfMonthZeroBased, []ref.Val{})
}),
),
),
Function(overloads.TimeGetDate,
MemberOverload(overloads.TimestampToDayOfMonthOneBased, []*Type{TimestampType}, IntType,
UnaryBinding(func(ts ref.Val) ref.Val {
t := ts.(types.Timestamp)
return t.Receive(overloads.TimeGetDate, overloads.TimestampToDayOfMonthOneBased, []ref.Val{})
}),
),
),
Function(overloads.TimeGetDayOfWeek,
MemberOverload(overloads.TimestampToDayOfWeek, []*Type{TimestampType}, IntType,
UnaryBinding(func(ts ref.Val) ref.Val {
t := ts.(types.Timestamp)
return t.Receive(overloads.TimeGetDayOfWeek, overloads.TimestampToDayOfWeek, []ref.Val{})
}),
),
),
Function(overloads.TimeGetHours,
MemberOverload(overloads.TimestampToHours, []*Type{TimestampType}, IntType,
UnaryBinding(func(ts ref.Val) ref.Val {
t := ts.(types.Timestamp)
return t.Receive(overloads.TimeGetHours, overloads.TimestampToHours, []ref.Val{})
}),
),
),
Function(overloads.TimeGetMinutes,
MemberOverload(overloads.TimestampToMinutes, []*Type{TimestampType}, IntType,
UnaryBinding(func(ts ref.Val) ref.Val {
t := ts.(types.Timestamp)
return t.Receive(overloads.TimeGetMinutes, overloads.TimestampToMinutes, []ref.Val{})
}),
),
),
Function(overloads.TimeGetSeconds,
MemberOverload(overloads.TimestampToSeconds, []*Type{TimestampType}, IntType,
UnaryBinding(func(ts ref.Val) ref.Val {
t := ts.(types.Timestamp)
return t.Receive(overloads.TimeGetSeconds, overloads.TimestampToSeconds, []ref.Val{})
}),
),
),
Function(overloads.TimeGetMilliseconds,
MemberOverload(overloads.TimestampToMilliseconds, []*Type{TimestampType}, IntType,
UnaryBinding(func(ts ref.Val) ref.Val {
t := ts.(types.Timestamp)
return t.Receive(overloads.TimeGetMilliseconds, overloads.TimestampToMilliseconds, []ref.Val{})
}),
),
),
}
)
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package cel
import (
"fmt"
"github.com/google/cel-go/common"
"github.com/google/cel-go/common/ast"
"github.com/google/cel-go/common/types"
"github.com/google/cel-go/parser"
exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1"
)
// Macro describes a function signature to match and the MacroExpander to apply.
//
// Note: when a Macro should apply to multiple overloads (based on arg count) of a given function,
// a Macro should be created per arg-count or as a var arg macro.
type Macro = parser.Macro
// MacroFactory defines an expansion function which converts a call and its arguments to a cel.Expr value.
type MacroFactory = parser.MacroExpander
// MacroExprFactory assists with the creation of Expr values in a manner which is consistent
// the internal semantics and id generation behaviors of the parser and checker libraries.
type MacroExprFactory = parser.ExprHelper
// MacroExpander converts a call and its associated arguments into a protobuf Expr representation.
//
// If the MacroExpander determines within the implementation that an expansion is not needed it may return
// a nil Expr value to indicate a non-match. However, if an expansion is to be performed, but the arguments
// are not well-formed, the result of the expansion will be an error.
//
// The MacroExpander accepts as arguments a MacroExprHelper as well as the arguments used in the function call
// and produces as output an Expr ast node.
//
// Note: when the Macro.IsReceiverStyle() method returns true, the target argument will be nil.
type MacroExpander func(eh MacroExprHelper, target *exprpb.Expr, args []*exprpb.Expr) (*exprpb.Expr, *Error)
// MacroExprHelper exposes helper methods for creating new expressions within a CEL abstract syntax tree.
// ExprHelper assists with the manipulation of proto-based Expr values in a manner which is
// consistent with the source position and expression id generation code leveraged by both
// the parser and type-checker.
type MacroExprHelper interface {
// Copy the input expression with a brand new set of identifiers.
Copy(*exprpb.Expr) *exprpb.Expr
// LiteralBool creates an Expr value for a bool literal.
LiteralBool(value bool) *exprpb.Expr
// LiteralBytes creates an Expr value for a byte literal.
LiteralBytes(value []byte) *exprpb.Expr
// LiteralDouble creates an Expr value for double literal.
LiteralDouble(value float64) *exprpb.Expr
// LiteralInt creates an Expr value for an int literal.
LiteralInt(value int64) *exprpb.Expr
// LiteralString creates am Expr value for a string literal.
LiteralString(value string) *exprpb.Expr
// LiteralUint creates an Expr value for a uint literal.
LiteralUint(value uint64) *exprpb.Expr
// NewList creates a CreateList instruction where the list is comprised of the optional set
// of elements provided as arguments.
NewList(elems ...*exprpb.Expr) *exprpb.Expr
// NewMap creates a CreateStruct instruction for a map where the map is comprised of the
// optional set of key, value entries.
NewMap(entries ...*exprpb.Expr_CreateStruct_Entry) *exprpb.Expr
// NewMapEntry creates a Map Entry for the key, value pair.
NewMapEntry(key *exprpb.Expr, val *exprpb.Expr, optional bool) *exprpb.Expr_CreateStruct_Entry
// NewObject creates a CreateStruct instruction for an object with a given type name and
// optional set of field initializers.
NewObject(typeName string, fieldInits ...*exprpb.Expr_CreateStruct_Entry) *exprpb.Expr
// NewObjectFieldInit creates a new Object field initializer from the field name and value.
NewObjectFieldInit(field string, init *exprpb.Expr, optional bool) *exprpb.Expr_CreateStruct_Entry
// Fold creates a fold comprehension instruction.
//
// - iterVar is the iteration variable name.
// - iterRange represents the expression that resolves to a list or map where the elements or
// keys (respectively) will be iterated over.
// - accuVar is the accumulation variable name, typically parser.AccumulatorName.
// - accuInit is the initial expression whose value will be set for the accuVar prior to
// folding.
// - condition is the expression to test to determine whether to continue folding.
// - step is the expression to evaluation at the conclusion of a single fold iteration.
// - result is the computation to evaluate at the conclusion of the fold.
//
// The accuVar should not shadow variable names that you would like to reference within the
// environment in the step and condition expressions. Presently, the name __result__ is commonly
// used by built-in macros but this may change in the future.
Fold(iterVar string,
iterRange *exprpb.Expr,
accuVar string,
accuInit *exprpb.Expr,
condition *exprpb.Expr,
step *exprpb.Expr,
result *exprpb.Expr) *exprpb.Expr
// Ident creates an identifier Expr value.
Ident(name string) *exprpb.Expr
// AccuIdent returns an accumulator identifier for use with comprehension results.
AccuIdent() *exprpb.Expr
// GlobalCall creates a function call Expr value for a global (free) function.
GlobalCall(function string, args ...*exprpb.Expr) *exprpb.Expr
// ReceiverCall creates a function call Expr value for a receiver-style function.
ReceiverCall(function string, target *exprpb.Expr, args ...*exprpb.Expr) *exprpb.Expr
// PresenceTest creates a Select TestOnly Expr value for modelling has() semantics.
PresenceTest(operand *exprpb.Expr, field string) *exprpb.Expr
// Select create a field traversal Expr value.
Select(operand *exprpb.Expr, field string) *exprpb.Expr
// OffsetLocation returns the Location of the expression identifier.
OffsetLocation(exprID int64) common.Location
// NewError associates an error message with a given expression id.
NewError(exprID int64, message string) *Error
}
// MacroOpt defines a functional option for configuring macro behavior.
type MacroOpt = parser.MacroOpt
// MacroDocs configures a list of strings into a multiline description for the macro.
func MacroDocs(docs ...string) MacroOpt {
return parser.MacroDocs(docs...)
}
// MacroExamples configures a list of examples, either as a string or common.MultilineString,
// into an example set to be provided with the macro Documentation() call.
func MacroExamples(examples ...string) MacroOpt {
return parser.MacroExamples(examples...)
}
// GlobalMacro creates a Macro for a global function with the specified arg count.
func GlobalMacro(function string, argCount int, factory MacroFactory, opts ...MacroOpt) Macro {
return parser.NewGlobalMacro(function, argCount, factory, opts...)
}
// ReceiverMacro creates a Macro for a receiver function matching the specified arg count.
func ReceiverMacro(function string, argCount int, factory MacroFactory, opts ...MacroOpt) Macro {
return parser.NewReceiverMacro(function, argCount, factory, opts...)
}
// GlobalVarArgMacro creates a Macro for a global function with a variable arg count.
func GlobalVarArgMacro(function string, factory MacroFactory, opts ...MacroOpt) Macro {
return parser.NewGlobalVarArgMacro(function, factory, opts...)
}
// ReceiverVarArgMacro creates a Macro for a receiver function matching a variable arg count.
func ReceiverVarArgMacro(function string, factory MacroFactory, opts ...MacroOpt) Macro {
return parser.NewReceiverVarArgMacro(function, factory, opts...)
}
// NewGlobalMacro creates a Macro for a global function with the specified arg count.
//
// Deprecated: use GlobalMacro
func NewGlobalMacro(function string, argCount int, expander MacroExpander) Macro {
expand := adaptingExpander{expander}
return parser.NewGlobalMacro(function, argCount, expand.Expander)
}
// NewReceiverMacro creates a Macro for a receiver function matching the specified arg count.
//
// Deprecated: use ReceiverMacro
func NewReceiverMacro(function string, argCount int, expander MacroExpander) Macro {
expand := adaptingExpander{expander}
return parser.NewReceiverMacro(function, argCount, expand.Expander)
}
// NewGlobalVarArgMacro creates a Macro for a global function with a variable arg count.
//
// Deprecated: use GlobalVarArgMacro
func NewGlobalVarArgMacro(function string, expander MacroExpander) Macro {
expand := adaptingExpander{expander}
return parser.NewGlobalVarArgMacro(function, expand.Expander)
}
// NewReceiverVarArgMacro creates a Macro for a receiver function matching a variable arg count.
//
// Deprecated: use ReceiverVarArgMacro
func NewReceiverVarArgMacro(function string, expander MacroExpander) Macro {
expand := adaptingExpander{expander}
return parser.NewReceiverVarArgMacro(function, expand.Expander)
}
// HasMacroExpander expands the input call arguments into a presence test, e.g. has(<operand>.field)
func HasMacroExpander(meh MacroExprHelper, target *exprpb.Expr, args []*exprpb.Expr) (*exprpb.Expr, *Error) {
ph, err := toParserHelper(meh)
if err != nil {
return nil, err
}
arg, err := adaptToExpr(args[0])
if err != nil {
return nil, err
}
if arg.Kind() == ast.SelectKind {
s := arg.AsSelect()
return adaptToProto(ph.NewPresenceTest(s.Operand(), s.FieldName()))
}
return nil, ph.NewError(arg.ID(), "invalid argument to has() macro")
}
// ExistsMacroExpander expands the input call arguments into a comprehension that returns true if any of the
// elements in the range match the predicate expressions:
// <iterRange>.exists(<iterVar>, <predicate>)
func ExistsMacroExpander(meh MacroExprHelper, target *exprpb.Expr, args []*exprpb.Expr) (*exprpb.Expr, *Error) {
ph, err := toParserHelper(meh)
if err != nil {
return nil, err
}
out, err := parser.MakeExists(ph, mustAdaptToExpr(target), mustAdaptToExprs(args))
if err != nil {
return nil, err
}
return adaptToProto(out)
}
// ExistsOneMacroExpander expands the input call arguments into a comprehension that returns true if exactly
// one of the elements in the range match the predicate expressions:
// <iterRange>.exists_one(<iterVar>, <predicate>)
func ExistsOneMacroExpander(meh MacroExprHelper, target *exprpb.Expr, args []*exprpb.Expr) (*exprpb.Expr, *Error) {
ph, err := toParserHelper(meh)
if err != nil {
return nil, err
}
out, err := parser.MakeExistsOne(ph, mustAdaptToExpr(target), mustAdaptToExprs(args))
if err != nil {
return nil, err
}
return adaptToProto(out)
}
// MapMacroExpander expands the input call arguments into a comprehension that transforms each element in the
// input to produce an output list.
//
// There are two call patterns supported by map:
//
// <iterRange>.map(<iterVar>, <transform>)
// <iterRange>.map(<iterVar>, <predicate>, <transform>)
//
// In the second form only iterVar values which return true when provided to the predicate expression
// are transformed.
func MapMacroExpander(meh MacroExprHelper, target *exprpb.Expr, args []*exprpb.Expr) (*exprpb.Expr, *Error) {
ph, err := toParserHelper(meh)
if err != nil {
return nil, err
}
out, err := parser.MakeMap(ph, mustAdaptToExpr(target), mustAdaptToExprs(args))
if err != nil {
return nil, err
}
return adaptToProto(out)
}
// FilterMacroExpander expands the input call arguments into a comprehension which produces a list which contains
// only elements which match the provided predicate expression:
// <iterRange>.filter(<iterVar>, <predicate>)
func FilterMacroExpander(meh MacroExprHelper, target *exprpb.Expr, args []*exprpb.Expr) (*exprpb.Expr, *Error) {
ph, err := toParserHelper(meh)
if err != nil {
return nil, err
}
out, err := parser.MakeFilter(ph, mustAdaptToExpr(target), mustAdaptToExprs(args))
if err != nil {
return nil, err
}
return adaptToProto(out)
}
var (
// Aliases to each macro in the CEL standard environment.
// Note: reassigning these macro variables may result in undefined behavior.
// HasMacro expands "has(m.f)" which tests the presence of a field, avoiding the need to
// specify the field as a string.
HasMacro = parser.HasMacro
// AllMacro expands "range.all(var, predicate)" into a comprehension which ensures that all
// elements in the range satisfy the predicate.
AllMacro = parser.AllMacro
// ExistsMacro expands "range.exists(var, predicate)" into a comprehension which ensures that
// some element in the range satisfies the predicate.
ExistsMacro = parser.ExistsMacro
// ExistsOneMacro expands "range.exists_one(var, predicate)", which is true if for exactly one
// element in range the predicate holds.
ExistsOneMacro = parser.ExistsOneMacro
// MapMacro expands "range.map(var, function)" into a comprehension which applies the function
// to each element in the range to produce a new list.
MapMacro = parser.MapMacro
// MapFilterMacro expands "range.map(var, predicate, function)" into a comprehension which
// first filters the elements in the range by the predicate, then applies the transform function
// to produce a new list.
MapFilterMacro = parser.MapFilterMacro
// FilterMacro expands "range.filter(var, predicate)" into a comprehension which filters
// elements in the range, producing a new list from the elements that satisfy the predicate.
FilterMacro = parser.FilterMacro
// StandardMacros provides an alias to all the CEL macros defined in the standard environment.
StandardMacros = []Macro{
HasMacro, AllMacro, ExistsMacro, ExistsOneMacro, MapMacro, MapFilterMacro, FilterMacro,
}
// NoMacros provides an alias to an empty list of macros
NoMacros = []Macro{}
)
type adaptingExpander struct {
legacyExpander MacroExpander
}
func (adapt *adaptingExpander) Expander(eh parser.ExprHelper, target ast.Expr, args []ast.Expr) (ast.Expr, *common.Error) {
var legacyTarget *exprpb.Expr = nil
var err *Error = nil
if target != nil {
legacyTarget, err = adaptToProto(target)
if err != nil {
return nil, err
}
}
legacyArgs := make([]*exprpb.Expr, len(args))
for i, arg := range args {
legacyArgs[i], err = adaptToProto(arg)
if err != nil {
return nil, err
}
}
ah := &adaptingHelper{modernHelper: eh}
legacyExpr, err := adapt.legacyExpander(ah, legacyTarget, legacyArgs)
if err != nil {
return nil, err
}
ex, err := adaptToExpr(legacyExpr)
if err != nil {
return nil, err
}
return ex, nil
}
func wrapErr(id int64, message string, err error) *common.Error {
return &common.Error{
Location: common.NoLocation,
Message: fmt.Sprintf("%s: %v", message, err),
ExprID: id,
}
}
type adaptingHelper struct {
modernHelper parser.ExprHelper
}
// Copy the input expression with a brand new set of identifiers.
func (ah *adaptingHelper) Copy(e *exprpb.Expr) *exprpb.Expr {
return mustAdaptToProto(ah.modernHelper.Copy(mustAdaptToExpr(e)))
}
// LiteralBool creates an Expr value for a bool literal.
func (ah *adaptingHelper) LiteralBool(value bool) *exprpb.Expr {
return mustAdaptToProto(ah.modernHelper.NewLiteral(types.Bool(value)))
}
// LiteralBytes creates an Expr value for a byte literal.
func (ah *adaptingHelper) LiteralBytes(value []byte) *exprpb.Expr {
return mustAdaptToProto(ah.modernHelper.NewLiteral(types.Bytes(value)))
}
// LiteralDouble creates an Expr value for double literal.
func (ah *adaptingHelper) LiteralDouble(value float64) *exprpb.Expr {
return mustAdaptToProto(ah.modernHelper.NewLiteral(types.Double(value)))
}
// LiteralInt creates an Expr value for an int literal.
func (ah *adaptingHelper) LiteralInt(value int64) *exprpb.Expr {
return mustAdaptToProto(ah.modernHelper.NewLiteral(types.Int(value)))
}
// LiteralString creates am Expr value for a string literal.
func (ah *adaptingHelper) LiteralString(value string) *exprpb.Expr {
return mustAdaptToProto(ah.modernHelper.NewLiteral(types.String(value)))
}
// LiteralUint creates an Expr value for a uint literal.
func (ah *adaptingHelper) LiteralUint(value uint64) *exprpb.Expr {
return mustAdaptToProto(ah.modernHelper.NewLiteral(types.Uint(value)))
}
// NewList creates a CreateList instruction where the list is comprised of the optional set
// of elements provided as arguments.
func (ah *adaptingHelper) NewList(elems ...*exprpb.Expr) *exprpb.Expr {
return mustAdaptToProto(ah.modernHelper.NewList(mustAdaptToExprs(elems)...))
}
// NewMap creates a CreateStruct instruction for a map where the map is comprised of the
// optional set of key, value entries.
func (ah *adaptingHelper) NewMap(entries ...*exprpb.Expr_CreateStruct_Entry) *exprpb.Expr {
adaptedEntries := make([]ast.EntryExpr, len(entries))
for i, e := range entries {
adaptedEntries[i] = mustAdaptToEntryExpr(e)
}
return mustAdaptToProto(ah.modernHelper.NewMap(adaptedEntries...))
}
// NewMapEntry creates a Map Entry for the key, value pair.
func (ah *adaptingHelper) NewMapEntry(key *exprpb.Expr, val *exprpb.Expr, optional bool) *exprpb.Expr_CreateStruct_Entry {
return mustAdaptToProtoEntry(
ah.modernHelper.NewMapEntry(mustAdaptToExpr(key), mustAdaptToExpr(val), optional))
}
// NewObject creates a CreateStruct instruction for an object with a given type name and
// optional set of field initializers.
func (ah *adaptingHelper) NewObject(typeName string, fieldInits ...*exprpb.Expr_CreateStruct_Entry) *exprpb.Expr {
adaptedEntries := make([]ast.EntryExpr, len(fieldInits))
for i, e := range fieldInits {
adaptedEntries[i] = mustAdaptToEntryExpr(e)
}
return mustAdaptToProto(ah.modernHelper.NewStruct(typeName, adaptedEntries...))
}
// NewObjectFieldInit creates a new Object field initializer from the field name and value.
func (ah *adaptingHelper) NewObjectFieldInit(field string, init *exprpb.Expr, optional bool) *exprpb.Expr_CreateStruct_Entry {
return mustAdaptToProtoEntry(
ah.modernHelper.NewStructField(field, mustAdaptToExpr(init), optional))
}
// Fold creates a fold comprehension instruction.
//
// - iterVar is the iteration variable name.
// - iterRange represents the expression that resolves to a list or map where the elements or
// keys (respectively) will be iterated over.
// - accuVar is the accumulation variable name, typically parser.AccumulatorName.
// - accuInit is the initial expression whose value will be set for the accuVar prior to
// folding.
// - condition is the expression to test to determine whether to continue folding.
// - step is the expression to evaluation at the conclusion of a single fold iteration.
// - result is the computation to evaluate at the conclusion of the fold.
//
// The accuVar should not shadow variable names that you would like to reference within the
// environment in the step and condition expressions. Presently, the name __result__ is commonly
// used by built-in macros but this may change in the future.
func (ah *adaptingHelper) Fold(iterVar string,
iterRange *exprpb.Expr,
accuVar string,
accuInit *exprpb.Expr,
condition *exprpb.Expr,
step *exprpb.Expr,
result *exprpb.Expr) *exprpb.Expr {
return mustAdaptToProto(
ah.modernHelper.NewComprehension(
mustAdaptToExpr(iterRange),
iterVar,
accuVar,
mustAdaptToExpr(accuInit),
mustAdaptToExpr(condition),
mustAdaptToExpr(step),
mustAdaptToExpr(result),
),
)
}
// Ident creates an identifier Expr value.
func (ah *adaptingHelper) Ident(name string) *exprpb.Expr {
return mustAdaptToProto(ah.modernHelper.NewIdent(name))
}
// AccuIdent returns an accumulator identifier for use with comprehension results.
func (ah *adaptingHelper) AccuIdent() *exprpb.Expr {
return mustAdaptToProto(ah.modernHelper.NewAccuIdent())
}
// GlobalCall creates a function call Expr value for a global (free) function.
func (ah *adaptingHelper) GlobalCall(function string, args ...*exprpb.Expr) *exprpb.Expr {
return mustAdaptToProto(ah.modernHelper.NewCall(function, mustAdaptToExprs(args)...))
}
// ReceiverCall creates a function call Expr value for a receiver-style function.
func (ah *adaptingHelper) ReceiverCall(function string, target *exprpb.Expr, args ...*exprpb.Expr) *exprpb.Expr {
return mustAdaptToProto(
ah.modernHelper.NewMemberCall(function, mustAdaptToExpr(target), mustAdaptToExprs(args)...))
}
// PresenceTest creates a Select TestOnly Expr value for modelling has() semantics.
func (ah *adaptingHelper) PresenceTest(operand *exprpb.Expr, field string) *exprpb.Expr {
op := mustAdaptToExpr(operand)
return mustAdaptToProto(ah.modernHelper.NewPresenceTest(op, field))
}
// Select create a field traversal Expr value.
func (ah *adaptingHelper) Select(operand *exprpb.Expr, field string) *exprpb.Expr {
op := mustAdaptToExpr(operand)
return mustAdaptToProto(ah.modernHelper.NewSelect(op, field))
}
// OffsetLocation returns the Location of the expression identifier.
func (ah *adaptingHelper) OffsetLocation(exprID int64) common.Location {
return ah.modernHelper.OffsetLocation(exprID)
}
// NewError associates an error message with a given expression id.
func (ah *adaptingHelper) NewError(exprID int64, message string) *Error {
return ah.modernHelper.NewError(exprID, message)
}
func mustAdaptToExprs(exprs []*exprpb.Expr) []ast.Expr {
adapted := make([]ast.Expr, len(exprs))
for i, e := range exprs {
adapted[i] = mustAdaptToExpr(e)
}
return adapted
}
func mustAdaptToExpr(e *exprpb.Expr) ast.Expr {
out, _ := adaptToExpr(e)
return out
}
func adaptToExpr(e *exprpb.Expr) (ast.Expr, *Error) {
if e == nil {
return nil, nil
}
out, err := ast.ProtoToExpr(e)
if err != nil {
return nil, wrapErr(e.GetId(), "proto conversion failure", err)
}
return out, nil
}
func mustAdaptToEntryExpr(e *exprpb.Expr_CreateStruct_Entry) ast.EntryExpr {
out, _ := ast.ProtoToEntryExpr(e)
return out
}
func mustAdaptToProto(e ast.Expr) *exprpb.Expr {
out, _ := adaptToProto(e)
return out
}
func adaptToProto(e ast.Expr) (*exprpb.Expr, *Error) {
if e == nil {
return nil, nil
}
out, err := ast.ExprToProto(e)
if err != nil {
return nil, wrapErr(e.ID(), "expr conversion failure", err)
}
return out, nil
}
func mustAdaptToProtoEntry(e ast.EntryExpr) *exprpb.Expr_CreateStruct_Entry {
out, _ := ast.EntryExprToProto(e)
return out
}
func toParserHelper(meh MacroExprHelper) (parser.ExprHelper, *Error) {
ah, ok := meh.(*adaptingHelper)
if !ok {
return nil, common.NewError(0,
fmt.Sprintf("unsupported macro helper: %v (%T)", meh, meh),
common.NoLocation)
}
return ah.modernHelper, nil
}
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package cel
import (
"sort"
"github.com/google/cel-go/common"
"github.com/google/cel-go/common/ast"
"github.com/google/cel-go/common/types"
"github.com/google/cel-go/common/types/ref"
)
// StaticOptimizer contains a sequence of ASTOptimizer instances which will be applied in order.
//
// The static optimizer normalizes expression ids and type-checking run between optimization
// passes to ensure that the final optimized output is a valid expression with metadata consistent
// with what would have been generated from a parsed and checked expression.
//
// Note: source position information is best-effort and likely wrong, but optimized expressions
// should be suitable for calls to parser.Unparse.
type StaticOptimizer struct {
optimizers []ASTOptimizer
}
// NewStaticOptimizer creates a StaticOptimizer with a sequence of ASTOptimizer's to be applied
// to a checked expression.
func NewStaticOptimizer(optimizers ...ASTOptimizer) *StaticOptimizer {
return &StaticOptimizer{
optimizers: optimizers,
}
}
// Optimize applies a sequence of optimizations to an Ast within a given environment.
//
// If issues are encountered, the Issues.Err() return value will be non-nil.
func (opt *StaticOptimizer) Optimize(env *Env, a *Ast) (*Ast, *Issues) {
// Make a copy of the AST to be optimized.
optimized := ast.Copy(a.NativeRep())
ids := newIDGenerator(ast.MaxID(a.NativeRep()))
// Create the optimizer context, could be pooled in the future.
issues := NewIssues(common.NewErrors(a.Source()))
baseFac := ast.NewExprFactory()
exprFac := &optimizerExprFactory{
idGenerator: ids,
fac: baseFac,
sourceInfo: optimized.SourceInfo(),
}
ctx := &OptimizerContext{
optimizerExprFactory: exprFac,
Env: env,
Issues: issues,
}
// Apply the optimizations sequentially.
for _, o := range opt.optimizers {
optimized = o.Optimize(ctx, optimized)
if issues.Err() != nil {
return nil, issues
}
// Normalize expression id metadata including coordination with macro call metadata.
freshIDGen := newIDGenerator(0)
info := optimized.SourceInfo()
expr := optimized.Expr()
normalizeIDs(freshIDGen.renumberStable, expr, info)
cleanupMacroRefs(expr, info)
// Recheck the updated expression for any possible type-agreement or validation errors.
parsed := &Ast{
source: a.Source(),
impl: ast.NewAST(expr, info)}
checked, iss := ctx.Check(parsed)
if iss.Err() != nil {
return nil, iss
}
optimized = checked.NativeRep()
}
// Return the optimized result.
return &Ast{
source: a.Source(),
impl: optimized,
}, nil
}
// normalizeIDs ensures that the metadata present with an AST is reset in a manner such
// that the ids within the expression correspond to the ids within macros.
func normalizeIDs(idGen ast.IDGenerator, optimized ast.Expr, info *ast.SourceInfo) {
optimized.RenumberIDs(idGen)
if len(info.MacroCalls()) == 0 {
return
}
// Sort the macro ids to make sure that the renumbering of macro-specific variables
// is stable across normalization calls.
sortedMacroIDs := []int64{}
for id := range info.MacroCalls() {
sortedMacroIDs = append(sortedMacroIDs, id)
}
sort.Slice(sortedMacroIDs, func(i, j int) bool { return sortedMacroIDs[i] < sortedMacroIDs[j] })
// First, update the macro call ids themselves.
callIDMap := map[int64]int64{}
for _, id := range sortedMacroIDs {
callIDMap[id] = idGen(id)
}
// Then update the macro call definitions which refer to these ids, but
// ensure that the updates don't collide and remove macro entries which haven't
// been visited / updated yet.
type macroUpdate struct {
id int64
call ast.Expr
}
macroUpdates := []macroUpdate{}
for _, oldID := range sortedMacroIDs {
newID := callIDMap[oldID]
call, found := info.GetMacroCall(oldID)
if !found {
continue
}
call.RenumberIDs(idGen)
macroUpdates = append(macroUpdates, macroUpdate{id: newID, call: call})
info.ClearMacroCall(oldID)
}
for _, u := range macroUpdates {
info.SetMacroCall(u.id, u.call)
}
}
func cleanupMacroRefs(expr ast.Expr, info *ast.SourceInfo) {
if len(info.MacroCalls()) == 0 {
return
}
// Sanitize the macro call references once the optimized expression has been computed
// and the ids normalized between the expression and the macros.
exprRefMap := make(map[int64]struct{})
ast.PostOrderVisit(expr, ast.NewExprVisitor(func(e ast.Expr) {
if e.ID() == 0 {
return
}
exprRefMap[e.ID()] = struct{}{}
}))
// Update the macro call id references to ensure that macro pointers are
// updated consistently across macros.
for _, call := range info.MacroCalls() {
ast.PostOrderVisit(call, ast.NewExprVisitor(func(e ast.Expr) {
if e.ID() == 0 {
return
}
exprRefMap[e.ID()] = struct{}{}
}))
}
for id := range info.MacroCalls() {
if _, found := exprRefMap[id]; !found {
info.ClearMacroCall(id)
}
}
}
// newIDGenerator ensures that new ids are only created the first time they are encountered.
func newIDGenerator(seed int64) *idGenerator {
return &idGenerator{
idMap: make(map[int64]int64),
seed: seed,
}
}
type idGenerator struct {
idMap map[int64]int64
seed int64
}
func (gen *idGenerator) nextID() int64 {
gen.seed++
return gen.seed
}
func (gen *idGenerator) renumberStable(id int64) int64 {
if id == 0 {
return 0
}
if newID, found := gen.idMap[id]; found {
return newID
}
nextID := gen.nextID()
gen.idMap[id] = nextID
return nextID
}
// OptimizerContext embeds Env and Issues instances to make it easy to type-check and evaluate
// subexpressions and report any errors encountered along the way. The context also embeds the
// optimizerExprFactory which can be used to generate new sub-expressions with expression ids
// consistent with the expectations of a parsed expression.
type OptimizerContext struct {
*Env
*optimizerExprFactory
*Issues
}
// ExtendEnv auguments the context's environment with the additional options.
func (opt *OptimizerContext) ExtendEnv(opts ...EnvOption) error {
e, err := opt.Env.Extend(opts...)
if err != nil {
return err
}
opt.Env = e
return nil
}
// ASTOptimizer applies an optimization over an AST and returns the optimized result.
type ASTOptimizer interface {
// Optimize optimizes a type-checked AST within an Environment and accumulates any issues.
Optimize(*OptimizerContext, *ast.AST) *ast.AST
}
type optimizerExprFactory struct {
*idGenerator
fac ast.ExprFactory
sourceInfo *ast.SourceInfo
}
// NewAST creates an AST from the current expression using the tracked source info which
// is modified and managed by the OptimizerContext.
func (opt *optimizerExprFactory) NewAST(expr ast.Expr) *ast.AST {
return ast.NewAST(expr, opt.sourceInfo)
}
// CopyAST creates a renumbered copy of `Expr` and `SourceInfo` values of the input AST, where the
// renumbering uses the same scheme as the core optimizer logic ensuring there are no collisions
// between copies.
//
// Use this method before attempting to merge the expression from AST into another.
func (opt *optimizerExprFactory) CopyAST(a *ast.AST) (ast.Expr, *ast.SourceInfo) {
idGen := newIDGenerator(opt.nextID())
defer func() { opt.seed = idGen.nextID() }()
copyExpr := opt.fac.CopyExpr(a.Expr())
copyInfo := ast.CopySourceInfo(a.SourceInfo())
normalizeIDs(idGen.renumberStable, copyExpr, copyInfo)
return copyExpr, copyInfo
}
// CopyASTAndMetadata copies the input AST and propagates the macro metadata into the AST being
// optimized.
func (opt *optimizerExprFactory) CopyASTAndMetadata(a *ast.AST) ast.Expr {
copyExpr, copyInfo := opt.CopyAST(a)
for macroID, call := range copyInfo.MacroCalls() {
opt.SetMacroCall(macroID, call)
}
return copyExpr
}
// ClearMacroCall clears the macro at the given expression id.
func (opt *optimizerExprFactory) ClearMacroCall(id int64) {
opt.sourceInfo.ClearMacroCall(id)
}
// SetMacroCall sets the macro call metadata for the given macro id within the tracked source info
// metadata.
func (opt *optimizerExprFactory) SetMacroCall(id int64, expr ast.Expr) {
opt.sourceInfo.SetMacroCall(id, expr)
}
// MacroCalls returns the map of macro calls currently in the context.
func (opt *optimizerExprFactory) MacroCalls() map[int64]ast.Expr {
return opt.sourceInfo.MacroCalls()
}
// NewBindMacro creates an AST expression representing the expanded bind() macro, and a macro expression
// representing the unexpanded call signature to be inserted into the source info macro call metadata.
func (opt *optimizerExprFactory) NewBindMacro(macroID int64, varName string, varInit, remaining ast.Expr) (astExpr, macroExpr ast.Expr) {
varID := opt.nextID()
remainingID := opt.nextID()
remaining = opt.fac.CopyExpr(remaining)
remaining.RenumberIDs(func(id int64) int64 {
if id == macroID {
return remainingID
}
return id
})
if call, exists := opt.sourceInfo.GetMacroCall(macroID); exists {
opt.SetMacroCall(remainingID, opt.fac.CopyExpr(call))
}
astExpr = opt.fac.NewComprehension(macroID,
opt.fac.NewList(opt.nextID(), []ast.Expr{}, []int32{}),
"#unused",
varName,
opt.fac.CopyExpr(varInit),
opt.fac.NewLiteral(opt.nextID(), types.False),
opt.fac.NewIdent(varID, varName),
remaining)
macroExpr = opt.fac.NewMemberCall(0, "bind",
opt.fac.NewIdent(opt.nextID(), "cel"),
opt.fac.NewIdent(varID, varName),
opt.fac.CopyExpr(varInit),
opt.fac.CopyExpr(remaining))
opt.sanitizeMacro(macroID, macroExpr)
return
}
// NewCall creates a global function call invocation expression.
//
// Example:
//
// countByField(list, fieldName)
// - function: countByField
// - args: [list, fieldName]
func (opt *optimizerExprFactory) NewCall(function string, args ...ast.Expr) ast.Expr {
return opt.fac.NewCall(opt.nextID(), function, args...)
}
// NewMemberCall creates a member function call invocation expression where 'target' is the receiver of the call.
//
// Example:
//
// list.countByField(fieldName)
// - function: countByField
// - target: list
// - args: [fieldName]
func (opt *optimizerExprFactory) NewMemberCall(function string, target ast.Expr, args ...ast.Expr) ast.Expr {
return opt.fac.NewMemberCall(opt.nextID(), function, target, args...)
}
// NewIdent creates a new identifier expression.
//
// Examples:
//
// - simple_var_name
// - qualified.subpackage.var_name
func (opt *optimizerExprFactory) NewIdent(name string) ast.Expr {
return opt.fac.NewIdent(opt.nextID(), name)
}
// NewLiteral creates a new literal expression value.
//
// The range of valid values for a literal generated during optimization is different than for expressions
// generated via parsing / type-checking, as the ref.Val may be _any_ CEL value so long as the value can
// be converted back to a literal-like form.
func (opt *optimizerExprFactory) NewLiteral(value ref.Val) ast.Expr {
return opt.fac.NewLiteral(opt.nextID(), value)
}
// NewList creates a list expression with a set of optional indices.
//
// Examples:
//
// [a, b]
// - elems: [a, b]
// - optIndices: []
//
// [a, ?b, ?c]
// - elems: [a, b, c]
// - optIndices: [1, 2]
func (opt *optimizerExprFactory) NewList(elems []ast.Expr, optIndices []int32) ast.Expr {
return opt.fac.NewList(opt.nextID(), elems, optIndices)
}
// NewMap creates a map from a set of entry expressions which contain a key and value expression.
func (opt *optimizerExprFactory) NewMap(entries []ast.EntryExpr) ast.Expr {
return opt.fac.NewMap(opt.nextID(), entries)
}
// NewMapEntry creates a map entry with a key and value expression and a flag to indicate whether the
// entry is optional.
//
// Examples:
//
// {a: b}
// - key: a
// - value: b
// - optional: false
//
// {?a: ?b}
// - key: a
// - value: b
// - optional: true
func (opt *optimizerExprFactory) NewMapEntry(key, value ast.Expr, isOptional bool) ast.EntryExpr {
return opt.fac.NewMapEntry(opt.nextID(), key, value, isOptional)
}
// NewHasMacro generates a test-only select expression to be included within an AST and an unexpanded
// has() macro call signature to be inserted into the source info macro call metadata.
func (opt *optimizerExprFactory) NewHasMacro(macroID int64, s ast.Expr) (astExpr, macroExpr ast.Expr) {
sel := s.AsSelect()
astExpr = opt.fac.NewPresenceTest(macroID, sel.Operand(), sel.FieldName())
macroExpr = opt.fac.NewCall(0, "has",
opt.NewSelect(opt.fac.CopyExpr(sel.Operand()), sel.FieldName()))
opt.sanitizeMacro(macroID, macroExpr)
return
}
// NewSelect creates a select expression where a field value is selected from an operand.
//
// Example:
//
// msg.field_name
// - operand: msg
// - field: field_name
func (opt *optimizerExprFactory) NewSelect(operand ast.Expr, field string) ast.Expr {
return opt.fac.NewSelect(opt.nextID(), operand, field)
}
// NewStruct creates a new typed struct value with an set of field initializations.
//
// Example:
//
// pkg.TypeName{field: value}
// - typeName: pkg.TypeName
// - fields: [{field: value}]
func (opt *optimizerExprFactory) NewStruct(typeName string, fields []ast.EntryExpr) ast.Expr {
return opt.fac.NewStruct(opt.nextID(), typeName, fields)
}
// NewStructField creates a struct field initialization.
//
// Examples:
//
// {count: 3u}
// - field: count
// - value: 3u
// - optional: false
//
// {?count: x}
// - field: count
// - value: x
// - optional: true
func (opt *optimizerExprFactory) NewStructField(field string, value ast.Expr, isOptional bool) ast.EntryExpr {
return opt.fac.NewStructField(opt.nextID(), field, value, isOptional)
}
// UpdateExpr updates the target expression with the updated content while preserving macro metadata.
//
// There are four scenarios during the update to consider:
// 1. target is not macro, updated is not macro
// 2. target is macro, updated is not macro
// 3. target is macro, updated is macro
// 4. target is not macro, updated is macro
//
// When the target is a macro already, it may either be updated to a new macro function
// body if the update is also a macro, or it may be removed altogether if the update is
// a macro.
//
// When the update is a macro, then the target references within other macros must be
// updated to point to the new updated macro. Otherwise, other macros which pointed to
// the target body must be replaced with copies of the updated expression body.
func (opt *optimizerExprFactory) UpdateExpr(target, updated ast.Expr) {
// Update the expression
target.SetKindCase(updated)
// Early return if there's no macros present sa the source info reflects the
// macro set from the target and updated expressions.
if len(opt.sourceInfo.MacroCalls()) == 0 {
return
}
// Determine whether the target expression was a macro.
_, targetIsMacro := opt.sourceInfo.GetMacroCall(target.ID())
// Determine whether the updated expression was a macro.
updatedMacro, updatedIsMacro := opt.sourceInfo.GetMacroCall(updated.ID())
if updatedIsMacro {
// If the updated call was a macro, then updated id maps to target id,
// and the updated macro moves into the target id slot.
opt.sourceInfo.ClearMacroCall(updated.ID())
opt.sourceInfo.SetMacroCall(target.ID(), updatedMacro)
} else if targetIsMacro {
// Otherwise if the target expr was a macro, but is no longer, clear
// the macro reference.
opt.sourceInfo.ClearMacroCall(target.ID())
}
// Punch holes in the updated value where macros references exist.
macroExpr := opt.fac.CopyExpr(target)
macroRefVisitor := ast.NewExprVisitor(func(e ast.Expr) {
if _, exists := opt.sourceInfo.GetMacroCall(e.ID()); exists {
e.SetKindCase(nil)
}
})
ast.PostOrderVisit(macroExpr, macroRefVisitor)
// Update any references to the expression within a macro
macroVisitor := ast.NewExprVisitor(func(call ast.Expr) {
// Update the target expression to point to the macro expression which
// will be empty if the updated expression was a macro.
if call.ID() == target.ID() {
call.SetKindCase(opt.fac.CopyExpr(macroExpr))
}
// Update the macro call expression if it refers to the updated expression
// id which has since been remapped to the target id.
if call.ID() == updated.ID() {
// Either ensure the expression is a macro reference or a populated with
// the relevant sub-expression if the updated expr was not a macro.
if updatedIsMacro {
call.SetKindCase(nil)
} else {
call.SetKindCase(opt.fac.CopyExpr(macroExpr))
}
// Since SetKindCase does not renumber the id, ensure the references to
// the old 'updated' id are mapped to the target id.
call.RenumberIDs(func(id int64) int64 {
if id == updated.ID() {
return target.ID()
}
return id
})
}
})
for _, call := range opt.sourceInfo.MacroCalls() {
ast.PostOrderVisit(call, macroVisitor)
}
}
func (opt *optimizerExprFactory) sanitizeMacro(macroID int64, macroExpr ast.Expr) {
macroRefVisitor := ast.NewExprVisitor(func(e ast.Expr) {
if _, exists := opt.sourceInfo.GetMacroCall(e.ID()); exists && e.ID() != macroID {
e.SetKindCase(nil)
}
})
ast.PostOrderVisit(macroExpr, macroRefVisitor)
}
// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package cel
import (
"errors"
"fmt"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/reflect/protodesc"
"google.golang.org/protobuf/reflect/protoreflect"
"google.golang.org/protobuf/reflect/protoregistry"
"google.golang.org/protobuf/types/dynamicpb"
"github.com/google/cel-go/checker"
"github.com/google/cel-go/common/containers"
"github.com/google/cel-go/common/decls"
"github.com/google/cel-go/common/env"
"github.com/google/cel-go/common/functions"
"github.com/google/cel-go/common/types"
"github.com/google/cel-go/common/types/pb"
"github.com/google/cel-go/common/types/ref"
"github.com/google/cel-go/interpreter"
"github.com/google/cel-go/parser"
exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1"
descpb "google.golang.org/protobuf/types/descriptorpb"
)
// These constants beginning with "Feature" enable optional behavior in
// the library. See the documentation for each constant to see its
// effects, compatibility restrictions, and standard conformance.
const (
_ = iota
// Enable the tracking of function call expressions replaced by macros.
featureEnableMacroCallTracking
// Enable the use of cross-type numeric comparisons at the type-checker.
featureCrossTypeNumericComparisons
// Enable eager validation of declarations to ensure that Env values created
// with `Extend` inherit a validated list of declarations from the parent Env.
featureEagerlyValidateDeclarations
// Enable the use of the default UTC timezone when a timezone is not specified
// on a CEL timestamp operation. This fixes the scenario where the input time
// is not already in UTC.
featureDefaultUTCTimeZone
// Enable the serialization of logical operator ASTs as variadic calls, thus
// compressing the logic graph to a single call when multiple like-operator
// expressions occur: e.g. a && b && c && d -> call(_&&_, [a, b, c, d])
featureVariadicLogicalASTs
// Enable error generation when a presence test or optional field selection is
// performed on a primitive type.
featureEnableErrorOnBadPresenceTest
// Enable escape syntax for field identifiers (`).
featureIdentEscapeSyntax
)
var featureIDsToNames = map[int]string{
featureEnableMacroCallTracking: "cel.feature.macro_call_tracking",
featureCrossTypeNumericComparisons: "cel.feature.cross_type_numeric_comparisons",
featureIdentEscapeSyntax: "cel.feature.backtick_escape_syntax",
}
func featureNameByID(id int) (string, bool) {
name, found := featureIDsToNames[id]
return name, found
}
func featureIDByName(name string) (int, bool) {
for id, n := range featureIDsToNames {
if n == name {
return id, true
}
}
return 0, false
}
// EnvOption is a functional interface for configuring the environment.
type EnvOption func(e *Env) (*Env, error)
// ClearMacros options clears all parser macros.
//
// Clearing macros will ensure CEL expressions can only contain linear evaluation paths, as
// comprehensions such as `all` and `exists` are enabled only via macros.
func ClearMacros() EnvOption {
return func(e *Env) (*Env, error) {
e.macros = NoMacros
return e, nil
}
}
// CustomTypeAdapter swaps the default types.Adapter implementation with a custom one.
//
// Note: This option must be specified before the Types and TypeDescs options when used together.
func CustomTypeAdapter(adapter types.Adapter) EnvOption {
return func(e *Env) (*Env, error) {
e.adapter = adapter
return e, nil
}
}
// CustomTypeProvider replaces the types.Provider implementation with a custom one.
//
// The `provider` variable type may either be types.Provider or ref.TypeProvider (deprecated)
//
// Note: This option must be specified before the Types and TypeDescs options when used together.
func CustomTypeProvider(provider any) EnvOption {
return func(e *Env) (*Env, error) {
var err error
e.provider, err = maybeInteropProvider(provider)
return e, err
}
}
// Declarations option extends the declaration set configured in the environment.
//
// Note: Declarations will by default be appended to the pre-existing declaration set configured
// for the environment. The NewEnv call builds on top of the standard CEL declarations. For a
// purely custom set of declarations use NewCustomEnv.
//
// Deprecated: use FunctionDecls and VariableDecls or FromConfig instead.
func Declarations(decls ...*exprpb.Decl) EnvOption {
declOpts := []EnvOption{}
var err error
var opt EnvOption
// Convert the declarations to `EnvOption` values ahead of time.
// Surface any errors in conversion when the options are applied.
for _, d := range decls {
opt, err = ExprDeclToDeclaration(d)
if err != nil {
break
}
declOpts = append(declOpts, opt)
}
return func(e *Env) (*Env, error) {
if err != nil {
return nil, err
}
for _, o := range declOpts {
e, err = o(e)
if err != nil {
return nil, err
}
}
return e, nil
}
}
// EagerlyValidateDeclarations ensures that any collisions between configured declarations are caught
// at the time of the `NewEnv` call.
//
// Eagerly validating declarations is also useful for bootstrapping a base `cel.Env` value.
// Calls to base `Env.Extend()` will be significantly faster when declarations are eagerly validated
// as declarations will be collision-checked at most once and only incrementally by way of `Extend`
//
// Disabled by default as not all environments are used for type-checking.
func EagerlyValidateDeclarations(enabled bool) EnvOption {
return features(featureEagerlyValidateDeclarations, enabled)
}
// HomogeneousAggregateLiterals disables mixed type list and map literal values.
//
// Note, it is still possible to have heterogeneous aggregates when provided as variables to the
// expression, as well as via conversion of well-known dynamic types, or with unchecked
// expressions.
func HomogeneousAggregateLiterals() EnvOption {
return ASTValidators(ValidateHomogeneousAggregateLiterals())
}
// variadicLogicalOperatorASTs flatten like-operator chained logical expressions into a single
// variadic call with N-terms. This behavior is useful when serializing to a protocol buffer as
// it will reduce the number of recursive calls needed to deserialize the AST later.
//
// For example, given the following expression the call graph will be rendered accordingly:
//
// expression: a && b && c && (d || e)
// ast: call(_&&_, [a, b, c, call(_||_, [d, e])])
func variadicLogicalOperatorASTs() EnvOption {
return features(featureVariadicLogicalASTs, true)
}
// Macros option extends the macro set configured in the environment.
//
// Note: This option must be specified after ClearMacros if used together.
func Macros(macros ...Macro) EnvOption {
return func(e *Env) (*Env, error) {
e.macros = append(e.macros, macros...)
return e, nil
}
}
// Container sets the container for resolving variable names. Defaults to an empty container.
//
// If all references within an expression are relative to a protocol buffer package, then
// specifying a container of `google.type` would make it possible to write expressions such as
// `Expr{expression: 'a < b'}` instead of having to write `google.type.Expr{...}`.
func Container(name string) EnvOption {
return func(e *Env) (*Env, error) {
cont, err := e.Container.Extend(containers.Name(name))
if err != nil {
return nil, err
}
e.Container = cont
return e, nil
}
}
// Abbrevs configures a set of simple names as abbreviations for fully-qualified names.
//
// An abbreviation (abbrev for short) is a simple name that expands to a fully-qualified name.
// Abbreviations can be useful when working with variables, functions, and especially types from
// multiple namespaces:
//
// // CEL object construction
// qual.pkg.version.ObjTypeName{
// field: alt.container.ver.FieldTypeName{value: ...}
// }
//
// Only one the qualified names above may be used as the CEL container, so at least one of these
// references must be a long qualified name within an otherwise short CEL program. Using the
// following abbreviations, the program becomes much simpler:
//
// // CEL Go option
// Abbrevs("qual.pkg.version.ObjTypeName", "alt.container.ver.FieldTypeName")
// // Simplified Object construction
// ObjTypeName{field: FieldTypeName{value: ...}}
//
// There are a few rules for the qualified names and the simple abbreviations generated from them:
// - Qualified names must be dot-delimited, e.g. `package.subpkg.name`.
// - The last element in the qualified name is the abbreviation.
// - Abbreviations must not collide with each other.
// - The abbreviation must not collide with unqualified names in use.
//
// Abbreviations are distinct from container-based references in the following important ways:
// - Abbreviations must expand to a fully-qualified name.
// - Expanded abbreviations do not participate in namespace resolution.
// - Abbreviation expansion is done instead of the container search for a matching identifier.
// - Containers follow C++ namespace resolution rules with searches from the most qualified name
//
// to the least qualified name.
//
// - Container references within the CEL program may be relative, and are resolved to fully
//
// qualified names at either type-check time or program plan time, whichever comes first.
//
// If there is ever a case where an identifier could be in both the container and as an
// abbreviation, the abbreviation wins as this will ensure that the meaning of a program is
// preserved between compilations even as the container evolves.
func Abbrevs(qualifiedNames ...string) EnvOption {
return func(e *Env) (*Env, error) {
cont, err := e.Container.Extend(containers.Abbrevs(qualifiedNames...))
if err != nil {
return nil, err
}
e.Container = cont
return e, nil
}
}
// customTypeRegistry is an internal-only interface containing the minimum methods required to support
// custom types. It is a subset of methods from ref.TypeRegistry.
type customTypeRegistry interface {
RegisterDescriptor(protoreflect.FileDescriptor) error
RegisterType(...ref.Type) error
}
// Types adds one or more type declarations to the environment, allowing for construction of
// type-literals whose definitions are included in the common expression built-in set.
//
// The input types may either be instances of `proto.Message` or `ref.Type`. Any other type
// provided to this option will result in an error.
//
// Well-known protobuf types within the `google.protobuf.*` package are included in the standard
// environment by default.
//
// Note: This option must be specified after the CustomTypeProvider option when used together.
func Types(addTypes ...any) EnvOption {
return func(e *Env) (*Env, error) {
reg, isReg := e.provider.(customTypeRegistry)
if !isReg {
return nil, fmt.Errorf("custom types not supported by provider: %T", e.provider)
}
for _, t := range addTypes {
switch v := t.(type) {
case proto.Message:
fdMap := pb.CollectFileDescriptorSet(v)
for _, fd := range fdMap {
err := reg.RegisterDescriptor(fd)
if err != nil {
return nil, err
}
}
case ref.Type:
err := reg.RegisterType(v)
if err != nil {
return nil, err
}
default:
return nil, fmt.Errorf("unsupported type: %T", t)
}
}
return e, nil
}
}
// TypeDescs adds type declarations from any protoreflect.FileDescriptor, protoregistry.Files,
// google.protobuf.FileDescriptorProto or google.protobuf.FileDescriptorSet provided.
//
// Note that messages instantiated from these descriptors will be *dynamicpb.Message values
// rather than the concrete message type.
//
// TypeDescs are hermetic to a single Env object, but may be copied to other Env values via
// extension or by re-using the same EnvOption with another NewEnv() call.
func TypeDescs(descs ...any) EnvOption {
return func(e *Env) (*Env, error) {
reg, isReg := e.provider.(customTypeRegistry)
if !isReg {
return nil, fmt.Errorf("custom types not supported by provider: %T", e.provider)
}
// Scan the input descriptors for FileDescriptorProto messages and accumulate them into a
// synthetic FileDescriptorSet as the FileDescriptorProto messages may refer to each other
// and will not resolve properly unless they are part of the same set.
var fds *descpb.FileDescriptorSet
for _, d := range descs {
switch f := d.(type) {
case *descpb.FileDescriptorProto:
if fds == nil {
fds = &descpb.FileDescriptorSet{
File: []*descpb.FileDescriptorProto{},
}
}
fds.File = append(fds.File, f)
}
}
if fds != nil {
if err := registerFileSet(reg, fds); err != nil {
return nil, err
}
}
for _, d := range descs {
switch f := d.(type) {
case *protoregistry.Files:
if err := registerFiles(reg, f); err != nil {
return nil, err
}
case protoreflect.FileDescriptor:
if err := reg.RegisterDescriptor(f); err != nil {
return nil, err
}
case *descpb.FileDescriptorSet:
if err := registerFileSet(reg, f); err != nil {
return nil, err
}
case *descpb.FileDescriptorProto:
// skip, handled as a synthetic file descriptor set.
default:
return nil, fmt.Errorf("unsupported type descriptor: %T", d)
}
}
return e, nil
}
}
func registerFileSet(reg customTypeRegistry, fileSet *descpb.FileDescriptorSet) error {
files, err := protodesc.NewFiles(fileSet)
if err != nil {
return fmt.Errorf("protodesc.NewFiles(%v) failed: %v", fileSet, err)
}
return registerFiles(reg, files)
}
func registerFiles(reg customTypeRegistry, files *protoregistry.Files) error {
var err error
files.RangeFiles(func(fd protoreflect.FileDescriptor) bool {
err = reg.RegisterDescriptor(fd)
return err == nil
})
return err
}
// ProgramOption is a functional interface for configuring evaluation bindings and behaviors.
type ProgramOption func(p *prog) (*prog, error)
// CustomDecorator appends an InterpreterDecorator to the program.
//
// InterpretableDecorators can be used to inspect, alter, or replace the Program plan.
func CustomDecorator(dec interpreter.InterpretableDecorator) ProgramOption {
return func(p *prog) (*prog, error) {
p.plannerOptions = append(p.plannerOptions, interpreter.CustomDecorator(dec))
return p, nil
}
}
// Functions adds function overloads that extend or override the set of CEL built-ins.
//
// Deprecated: use Function() instead to declare the function, its overload signatures,
// and the overload implementations.
func Functions(funcs ...*functions.Overload) ProgramOption {
return func(p *prog) (*prog, error) {
if err := p.dispatcher.Add(funcs...); err != nil {
return nil, err
}
return p, nil
}
}
// Globals sets the global variable values for a given program. These values may be shadowed by
// variables with the same name provided to the Eval() call. If Globals is used in a Library with
// a Lib EnvOption, vars may shadow variables provided by previously added libraries.
//
// The vars value may either be an `cel.Activation` instance or a `map[string]any`.
func Globals(vars any) ProgramOption {
return func(p *prog) (*prog, error) {
defaultVars, err := NewActivation(vars)
if err != nil {
return nil, err
}
if p.defaultVars != nil {
defaultVars = interpreter.NewHierarchicalActivation(p.defaultVars, defaultVars)
}
p.defaultVars = defaultVars
return p, nil
}
}
// OptimizeRegex provides a way to replace the InterpretableCall for regex functions. This can be used
// to compile regex string constants at program creation time and report any errors and then use the
// compiled regex for all regex function invocations.
func OptimizeRegex(regexOptimizations ...*interpreter.RegexOptimization) ProgramOption {
return func(p *prog) (*prog, error) {
p.regexOptimizations = append(p.regexOptimizations, regexOptimizations...)
return p, nil
}
}
// ConfigOptionFactory declares a signature which accepts a configuration element, e.g. env.Extension
// and optionally produces an EnvOption in response.
//
// If there are multiple ConfigOptionFactory values which could apply to the same configuration node
// the first one that returns an EnvOption and a `true` response will be used, and the config node
// will not be passed along to any other option factory.
//
// Only the *env.Extension type is provided at this time, but validators, optimizers, and other tuning
// parameters may be supported in the future.
type ConfigOptionFactory func(any) (EnvOption, bool)
// FromConfig produces and applies a set of EnvOption values derived from an env.Config object.
//
// For configuration elements which refer to features outside of the `cel` package, an optional set of
// ConfigOptionFactory values may be passed in to support the conversion from static configuration to
// configured cel.Env value.
//
// Note: disabling the standard library will clear the EnvOptions values previously set for the
// environment with the exception of propagating types and adapters over to the new environment.
//
// Note: to support custom types referenced in the configuration file, you must ensure that one of
// the following options appears before the FromConfig option: Types, TypeDescs, or CustomTypeProvider
// as the type provider configured at the time when the config is processed is the one used to derive
// type references from the configuration.
func FromConfig(config *env.Config, optFactories ...ConfigOptionFactory) EnvOption {
return func(e *Env) (*Env, error) {
if err := config.Validate(); err != nil {
return nil, err
}
opts, err := configToEnvOptions(config, e.CELTypeProvider(), optFactories)
if err != nil {
return nil, err
}
for _, o := range opts {
e, err = o(e)
if err != nil {
return nil, err
}
}
return e, nil
}
}
// configToEnvOptions generates a set of EnvOption values (or error) based on a config, a type provider,
// and an optional set of environment options.
func configToEnvOptions(config *env.Config, provider types.Provider, optFactories []ConfigOptionFactory) ([]EnvOption, error) {
envOpts := []EnvOption{}
// Configure the standard lib subset.
if config.StdLib != nil {
envOpts = append(envOpts, func(e *Env) (*Env, error) {
if e.HasLibrary("cel.lib.std") {
return nil, errors.New("invalid subset of stdlib: create a custom env")
}
return e, nil
})
if !config.StdLib.Disabled {
envOpts = append(envOpts, StdLib(StdLibSubset(config.StdLib)))
}
} else {
envOpts = append(envOpts, StdLib())
}
// Configure the container
if config.Container != "" {
envOpts = append(envOpts, Container(config.Container))
}
// Configure abbreviations
for _, imp := range config.Imports {
envOpts = append(envOpts, Abbrevs(imp.Name))
}
// Configure the context variable declaration
if config.ContextVariable != nil {
typeName := config.ContextVariable.TypeName
if _, found := provider.FindStructType(typeName); !found {
return nil, fmt.Errorf("invalid context proto type: %q", typeName)
}
// Attempt to instantiate the proto in order to reflect to its descriptor
msg := provider.NewValue(typeName, map[string]ref.Val{})
pbMsg, ok := msg.Value().(proto.Message)
if !ok {
return nil, fmt.Errorf("unsupported context type: %T", msg.Value())
}
envOpts = append(envOpts, DeclareContextProto(pbMsg.ProtoReflect().Descriptor()))
}
// Configure variables
if len(config.Variables) != 0 {
vars := make([]*decls.VariableDecl, 0, len(config.Variables))
for _, v := range config.Variables {
vDef, err := v.AsCELVariable(provider)
if err != nil {
return nil, err
}
vars = append(vars, vDef)
}
envOpts = append(envOpts, VariableDecls(vars...))
}
// Configure functions
if len(config.Functions) != 0 {
funcs := make([]*decls.FunctionDecl, 0, len(config.Functions))
for _, f := range config.Functions {
fnDef, err := f.AsCELFunction(provider)
if err != nil {
return nil, err
}
funcs = append(funcs, fnDef)
}
envOpts = append(envOpts, FunctionDecls(funcs...))
}
// Configure features
for _, feat := range config.Features {
// Note, if a feature is not found, it is skipped as it is possible the feature
// is not intended to be supported publicly. In the future, a refinement of
// to this strategy to report unrecognized features and validators should probably
// be covered as a standard ConfigOptionFactory
if id, found := featureIDByName(feat.Name); found {
envOpts = append(envOpts, features(id, feat.Enabled))
}
}
// Configure validators
for _, val := range config.Validators {
if fac, found := astValidatorFactories[val.Name]; found {
envOpts = append(envOpts, func(e *Env) (*Env, error) {
validator, err := fac(val)
if err != nil {
return nil, fmt.Errorf("%w", err)
}
return ASTValidators(validator)(e)
})
} else if opt, handled := handleExtendedConfigOption(val, optFactories); handled {
envOpts = append(envOpts, opt)
}
// we don't error when the validator isn't found as it may be part
// of an extension library and enabled implicitly.
}
// Configure extensions
for _, ext := range config.Extensions {
// version number has been validated by the call to `Validate`
ver, _ := ext.VersionNumber()
if ext.Name == "optional" {
envOpts = append(envOpts, OptionalTypes(OptionalTypesVersion(ver)))
} else {
opt, handled := handleExtendedConfigOption(ext, optFactories)
if !handled {
return nil, fmt.Errorf("unrecognized extension: %s", ext.Name)
}
envOpts = append(envOpts, opt)
}
}
return envOpts, nil
}
func handleExtendedConfigOption(conf any, optFactories []ConfigOptionFactory) (EnvOption, bool) {
for _, optFac := range optFactories {
if opt, useOption := optFac(conf); useOption {
return opt, true
}
}
return nil, false
}
// EvalOption indicates an evaluation option that may affect the evaluation behavior or information
// in the output result.
type EvalOption int
const (
// OptTrackState will cause the runtime to return an immutable EvalState value in the Result.
OptTrackState EvalOption = 1 << iota
// OptExhaustiveEval causes the runtime to disable short-circuits and track state.
OptExhaustiveEval EvalOption = 1<<iota | OptTrackState
// OptOptimize precomputes functions and operators with constants as arguments at program
// creation time. It also pre-compiles regex pattern constants passed to 'matches', reports any compilation errors
// at program creation and uses the compiled regex pattern for all 'matches' function invocations.
// This flag is useful when the expression will be evaluated repeatedly against
// a series of different inputs.
OptOptimize EvalOption = 1 << iota
// OptPartialEval enables the evaluation of a partial state where the input data that may be
// known to be missing, either as top-level variables, or somewhere within a variable's object
// member graph.
//
// By itself, OptPartialEval does not change evaluation behavior unless the input to the
// Program Eval() call is created via PartialVars().
OptPartialEval EvalOption = 1 << iota
// OptTrackCost enables the runtime cost calculation while validation and return cost within evalDetails
// cost calculation is available via func ActualCost()
OptTrackCost EvalOption = 1 << iota
// OptCheckStringFormat enables compile-time checking of string.format calls for syntax/cardinality.
//
// Deprecated: use ext.StringsValidateFormatCalls() as this option is now a no-op.
OptCheckStringFormat EvalOption = 1 << iota
)
// EvalOptions sets one or more evaluation options which may affect the evaluation or Result.
func EvalOptions(opts ...EvalOption) ProgramOption {
return func(p *prog) (*prog, error) {
for _, opt := range opts {
p.evalOpts |= opt
}
return p, nil
}
}
// InterruptCheckFrequency configures the number of iterations within a comprehension to evaluate
// before checking whether the function evaluation has been interrupted.
func InterruptCheckFrequency(checkFrequency uint) ProgramOption {
return func(p *prog) (*prog, error) {
p.interruptCheckFrequency = checkFrequency
return p, nil
}
}
// CostEstimatorOptions configure type-check time options for estimating expression cost.
func CostEstimatorOptions(costOpts ...checker.CostOption) EnvOption {
return func(e *Env) (*Env, error) {
e.costOptions = append(e.costOptions, costOpts...)
return e, nil
}
}
// CostTrackerOptions configures a set of options for cost-tracking.
//
// Note, CostTrackerOptions is a no-op unless CostTracking is also enabled.
func CostTrackerOptions(costOpts ...interpreter.CostTrackerOption) ProgramOption {
return func(p *prog) (*prog, error) {
p.costOptions = append(p.costOptions, costOpts...)
return p, nil
}
}
// CostTracking enables cost tracking and registers a ActualCostEstimator that can optionally provide a runtime cost estimate for any function calls.
func CostTracking(costEstimator interpreter.ActualCostEstimator) ProgramOption {
return func(p *prog) (*prog, error) {
p.callCostEstimator = costEstimator
p.evalOpts |= OptTrackCost
return p, nil
}
}
// CostLimit enables cost tracking and sets configures program evaluation to exit early with a
// "runtime cost limit exceeded" error if the runtime cost exceeds the costLimit.
// The CostLimit is a metric that corresponds to the number and estimated expense of operations
// performed while evaluating an expression. It is indicative of CPU usage, not memory usage.
func CostLimit(costLimit uint64) ProgramOption {
return func(p *prog) (*prog, error) {
p.costLimit = &costLimit
p.evalOpts |= OptTrackCost
return p, nil
}
}
func fieldToCELType(field protoreflect.FieldDescriptor) (*Type, error) {
if field.Kind() == protoreflect.MessageKind || field.Kind() == protoreflect.GroupKind {
msgName := (string)(field.Message().FullName())
return ObjectType(msgName), nil
}
if primitiveType, found := types.ProtoCELPrimitives[field.Kind()]; found {
return primitiveType, nil
}
if field.Kind() == protoreflect.EnumKind {
return IntType, nil
}
return nil, fmt.Errorf("field %s type %s not implemented", field.FullName(), field.Kind().String())
}
func fieldToVariable(field protoreflect.FieldDescriptor) (*decls.VariableDecl, error) {
name := string(field.Name())
if field.IsMap() {
mapKey := field.MapKey()
mapValue := field.MapValue()
keyType, err := fieldToCELType(mapKey)
if err != nil {
return nil, err
}
valueType, err := fieldToCELType(mapValue)
if err != nil {
return nil, err
}
return decls.NewVariable(name, MapType(keyType, valueType)), nil
}
if field.IsList() {
elemType, err := fieldToCELType(field)
if err != nil {
return nil, err
}
return decls.NewVariable(name, ListType(elemType)), nil
}
celType, err := fieldToCELType(field)
if err != nil {
return nil, err
}
return decls.NewVariable(name, celType), nil
}
// DeclareContextProto returns an option to extend CEL environment with declarations from the given context proto.
// Each field of the proto defines a variable of the same name in the environment.
// https://github.com/google/cel-spec/blob/master/doc/langdef.md#evaluation-environment
func DeclareContextProto(descriptor protoreflect.MessageDescriptor) EnvOption {
return func(e *Env) (*Env, error) {
if e.contextProto != nil {
return nil, fmt.Errorf("context proto already declared as %q, got %q",
e.contextProto.FullName(), descriptor.FullName())
}
e.contextProto = descriptor
fields := descriptor.Fields()
vars := make([]*decls.VariableDecl, 0, fields.Len())
for i := 0; i < fields.Len(); i++ {
field := fields.Get(i)
variable, err := fieldToVariable(field)
if err != nil {
return nil, err
}
vars = append(vars, variable)
}
var err error
e, err = VariableDecls(vars...)(e)
if err != nil {
return nil, err
}
return Types(dynamicpb.NewMessage(descriptor))(e)
}
}
// ContextProtoVars uses the fields of the input proto.Messages as top-level variables within an Activation.
//
// Consider using with `DeclareContextProto` to simplify variable type declarations and publishing when using
// protocol buffers.
func ContextProtoVars(ctx proto.Message) (Activation, error) {
if ctx == nil || !ctx.ProtoReflect().IsValid() {
return interpreter.EmptyActivation(), nil
}
reg, err := types.NewRegistry(ctx)
if err != nil {
return nil, err
}
pbRef := ctx.ProtoReflect()
typeName := string(pbRef.Descriptor().FullName())
fields := pbRef.Descriptor().Fields()
vars := make(map[string]any, fields.Len())
for i := 0; i < fields.Len(); i++ {
field := fields.Get(i)
sft, found := reg.FindStructFieldType(typeName, field.TextName())
if !found {
return nil, fmt.Errorf("no such field: %s", field.TextName())
}
fieldVal, err := sft.GetFrom(ctx)
if err != nil {
return nil, err
}
vars[field.TextName()] = fieldVal
}
return NewActivation(vars)
}
// EnableMacroCallTracking ensures that call expressions which are replaced by macros
// are tracked in the `SourceInfo` of parsed and checked expressions.
func EnableMacroCallTracking() EnvOption {
return features(featureEnableMacroCallTracking, true)
}
// EnableIdentifierEscapeSyntax enables identifier escaping (`) syntax for
// fields.
func EnableIdentifierEscapeSyntax() EnvOption {
return features(featureIdentEscapeSyntax, true)
}
// CrossTypeNumericComparisons makes it possible to compare across numeric types, e.g. double < int
func CrossTypeNumericComparisons(enabled bool) EnvOption {
return features(featureCrossTypeNumericComparisons, enabled)
}
// DefaultUTCTimeZone ensures that time-based operations use the UTC timezone rather than the
// input time's local timezone.
func DefaultUTCTimeZone(enabled bool) EnvOption {
return features(featureDefaultUTCTimeZone, enabled)
}
// features sets the given feature flags. See list of Feature constants above.
func features(flag int, enabled bool) EnvOption {
return func(e *Env) (*Env, error) {
e.features[flag] = enabled
return e, nil
}
}
// ParserRecursionLimit adjusts the AST depth the parser will tolerate.
// Defaults defined in the parser package.
func ParserRecursionLimit(limit int) EnvOption {
return func(e *Env) (*Env, error) {
e.prsrOpts = append(e.prsrOpts, parser.MaxRecursionDepth(limit))
return e, nil
}
}
// ParserExpressionSizeLimit adjusts the number of code points the expression parser is allowed to parse.
// Defaults defined in the parser package.
func ParserExpressionSizeLimit(limit int) EnvOption {
return func(e *Env) (*Env, error) {
e.prsrOpts = append(e.prsrOpts, parser.ExpressionSizeCodePointLimit(limit))
return e, nil
}
}
// EnableHiddenAccumulatorName sets the parser to use the identifier '@result' for accumulators
// which is not normally accessible from CEL source.
func EnableHiddenAccumulatorName(enabled bool) EnvOption {
return func(e *Env) (*Env, error) {
e.prsrOpts = append(e.prsrOpts, parser.EnableHiddenAccumulatorName(enabled))
return e, nil
}
}
func maybeInteropProvider(provider any) (types.Provider, error) {
switch p := provider.(type) {
case types.Provider:
return p, nil
case ref.TypeProvider:
return &interopCELTypeProvider{TypeProvider: p}, nil
default:
return nil, fmt.Errorf("unsupported type provider: %T", provider)
}
}
// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package cel
import (
"context"
"fmt"
"sync"
"github.com/google/cel-go/common/ast"
"github.com/google/cel-go/common/types"
"github.com/google/cel-go/common/types/ref"
"github.com/google/cel-go/interpreter"
)
// Program is an evaluable view of an Ast.
type Program interface {
// Eval returns the result of an evaluation of the Ast and environment against the input vars.
//
// The vars value may either be an `Activation` or a `map[string]any`.
//
// If the `OptTrackState`, `OptTrackCost` or `OptExhaustiveEval` flags are used, the `details` response will
// be non-nil. Given this caveat on `details`, the return state from evaluation will be:
//
// * `val`, `details`, `nil` - Successful evaluation of a non-error result.
// * `val`, `details`, `err` - Successful evaluation to an error result.
// * `nil`, `details`, `err` - Unsuccessful evaluation.
//
// An unsuccessful evaluation is typically the result of a series of incompatible `EnvOption`
// or `ProgramOption` values used in the creation of the evaluation environment or executable
// program.
Eval(any) (ref.Val, *EvalDetails, error)
// ContextEval evaluates the program with a set of input variables and a context object in order
// to support cancellation and timeouts. This method must be used in conjunction with the
// InterruptCheckFrequency() option for cancellation interrupts to be impact evaluation.
//
// The vars value may either be an `Activation` or `map[string]any`.
//
// The output contract for `ContextEval` is otherwise identical to the `Eval` method.
ContextEval(context.Context, any) (ref.Val, *EvalDetails, error)
}
// Activation used to resolve identifiers by name and references by id.
//
// An Activation is the primary mechanism by which a caller supplies input into a CEL program.
type Activation = interpreter.Activation
// NewActivation returns an activation based on a map-based binding where the map keys are
// expected to be qualified names used with ResolveName calls.
//
// The input `bindings` may either be of type `Activation` or `map[string]any`.
//
// Lazy bindings may be supplied within the map-based input in either of the following forms:
// - func() any
// - func() ref.Val
//
// The output of the lazy binding will overwrite the variable reference in the internal map.
//
// Values which are not represented as ref.Val types on input may be adapted to a ref.Val using
// the types.Adapter configured in the environment.
func NewActivation(bindings any) (Activation, error) {
return interpreter.NewActivation(bindings)
}
// PartialActivation extends the Activation interface with a set of unknown AttributePatterns.
type PartialActivation = interpreter.PartialActivation
// NoVars returns an empty Activation.
func NoVars() Activation {
return interpreter.EmptyActivation()
}
// PartialVars returns a PartialActivation which contains variables and a set of AttributePattern
// values that indicate variables or parts of variables whose value are not yet known.
//
// This method relies on manually configured sets of missing attribute patterns. For a method which
// infers the missing variables from the input and the configured environment, use Env.PartialVars().
//
// The `vars` value may either be an Activation or any valid input to the NewActivation call.
func PartialVars(vars any,
unknowns ...*AttributePatternType) (PartialActivation, error) {
return interpreter.NewPartialActivation(vars, unknowns...)
}
// AttributePattern returns an AttributePattern that matches a top-level variable. The pattern is
// mutable, and its methods support the specification of one or more qualifier patterns.
//
// For example, the AttributePattern(`a`).QualString(`b`) represents a variable access `a` with a
// string field or index qualification `b`. This pattern will match Attributes `a`, and `a.b`,
// but not `a.c`.
//
// When using a CEL expression within a container, e.g. a package or namespace, the variable name
// in the pattern must match the qualified name produced during the variable namespace resolution.
// For example, when variable `a` is declared within an expression whose container is `ns.app`, the
// fully qualified variable name may be `ns.app.a`, `ns.a`, or `a` per the CEL namespace resolution
// rules. Pick the fully qualified variable name that makes sense within the container as the
// AttributePattern `varName` argument.
func AttributePattern(varName string) *AttributePatternType {
return interpreter.NewAttributePattern(varName)
}
// AttributePatternType represents a top-level variable with an optional set of qualifier patterns.
//
// See the interpreter.AttributePattern and interpreter.AttributeQualifierPattern for more info
// about how to create and manipulate AttributePattern values.
type AttributePatternType = interpreter.AttributePattern
// EvalDetails holds additional information observed during the Eval() call.
type EvalDetails struct {
state interpreter.EvalState
costTracker *interpreter.CostTracker
}
// State of the evaluation, non-nil if the OptTrackState or OptExhaustiveEval is specified
// within EvalOptions.
func (ed *EvalDetails) State() interpreter.EvalState {
if ed == nil {
return interpreter.NewEvalState()
}
return ed.state
}
// ActualCost returns the tracked cost through the course of execution when `CostTracking` is enabled.
// Otherwise, returns nil if the cost was not enabled.
func (ed *EvalDetails) ActualCost() *uint64 {
if ed == nil || ed.costTracker == nil {
return nil
}
cost := ed.costTracker.ActualCost()
return &cost
}
// prog is the internal implementation of the Program interface.
type prog struct {
*Env
evalOpts EvalOption
defaultVars Activation
dispatcher interpreter.Dispatcher
interpreter interpreter.Interpreter
interruptCheckFrequency uint
// Intermediate state used to configure the InterpretableDecorator set provided
// to the initInterpretable call.
plannerOptions []interpreter.PlannerOption
regexOptimizations []*interpreter.RegexOptimization
// Interpretable configured from an Ast and aggregate decorator set based on program options.
interpretable interpreter.Interpretable
observable *interpreter.ObservableInterpretable
callCostEstimator interpreter.ActualCostEstimator
costOptions []interpreter.CostTrackerOption
costLimit *uint64
}
// newProgram creates a program instance with an environment, an ast, and an optional list of
// ProgramOption values.
//
// If the program cannot be configured the prog will be nil, with a non-nil error response.
func newProgram(e *Env, a *ast.AST, opts []ProgramOption) (Program, error) {
// Build the dispatcher, interpreter, and default program value.
disp := interpreter.NewDispatcher()
// Ensure the default attribute factory is set after the adapter and provider are
// configured.
p := &prog{
Env: e,
plannerOptions: []interpreter.PlannerOption{},
dispatcher: disp,
costOptions: []interpreter.CostTrackerOption{},
}
// Configure the program via the ProgramOption values.
var err error
for _, opt := range opts {
p, err = opt(p)
if err != nil {
return nil, err
}
}
// Add the function bindings created via Function() options.
for _, fn := range e.functions {
bindings, err := fn.Bindings()
if err != nil {
return nil, err
}
err = disp.Add(bindings...)
if err != nil {
return nil, err
}
}
// Set the attribute factory after the options have been set.
var attrFactory interpreter.AttributeFactory
attrFactorOpts := []interpreter.AttrFactoryOption{
interpreter.EnableErrorOnBadPresenceTest(p.HasFeature(featureEnableErrorOnBadPresenceTest)),
}
if p.evalOpts&OptPartialEval == OptPartialEval {
attrFactory = interpreter.NewPartialAttributeFactory(e.Container, e.adapter, e.provider, attrFactorOpts...)
} else {
attrFactory = interpreter.NewAttributeFactory(e.Container, e.adapter, e.provider, attrFactorOpts...)
}
interp := interpreter.NewInterpreter(disp, e.Container, e.provider, e.adapter, attrFactory)
p.interpreter = interp
// Translate the EvalOption flags into InterpretableDecorator instances.
plannerOptions := make([]interpreter.PlannerOption, len(p.plannerOptions))
copy(plannerOptions, p.plannerOptions)
// Enable interrupt checking if there's a non-zero check frequency
if p.interruptCheckFrequency > 0 {
plannerOptions = append(plannerOptions, interpreter.InterruptableEval())
}
// Enable constant folding first.
if p.evalOpts&OptOptimize == OptOptimize {
plannerOptions = append(plannerOptions, interpreter.Optimize())
p.regexOptimizations = append(p.regexOptimizations, interpreter.MatchesRegexOptimization)
}
// Enable regex compilation of constants immediately after folding constants.
if len(p.regexOptimizations) > 0 {
plannerOptions = append(plannerOptions, interpreter.CompileRegexConstants(p.regexOptimizations...))
}
// Enable exhaustive eval, state tracking and cost tracking last since they require a factory.
if p.evalOpts&(OptExhaustiveEval|OptTrackState|OptTrackCost) != 0 {
costOptCount := len(p.costOptions)
if p.costLimit != nil {
costOptCount++
}
costOpts := make([]interpreter.CostTrackerOption, 0, costOptCount)
costOpts = append(costOpts, p.costOptions...)
if p.costLimit != nil {
costOpts = append(costOpts, interpreter.CostTrackerLimit(*p.costLimit))
}
trackerFactory := func() (*interpreter.CostTracker, error) {
return interpreter.NewCostTracker(p.callCostEstimator, costOpts...)
}
var observers []interpreter.PlannerOption
if p.evalOpts&(OptExhaustiveEval|OptTrackState) != 0 {
// EvalStateObserver is required for OptExhaustiveEval.
observers = append(observers, interpreter.EvalStateObserver())
}
if p.evalOpts&OptTrackCost == OptTrackCost {
observers = append(observers, interpreter.CostObserver(interpreter.CostTrackerFactory(trackerFactory)))
}
// Enable exhaustive eval over a basic observer since it offers a superset of features.
if p.evalOpts&OptExhaustiveEval == OptExhaustiveEval {
plannerOptions = append(plannerOptions,
append([]interpreter.PlannerOption{interpreter.ExhaustiveEval()}, observers...)...)
} else if len(observers) > 0 {
plannerOptions = append(plannerOptions, observers...)
}
}
return p.initInterpretable(a, plannerOptions)
}
func (p *prog) initInterpretable(a *ast.AST, plannerOptions []interpreter.PlannerOption) (*prog, error) {
// When the AST has been exprAST it contains metadata that can be used to speed up program execution.
interpretable, err := p.interpreter.NewInterpretable(a, plannerOptions...)
if err != nil {
return nil, err
}
p.interpretable = interpretable
if oi, ok := interpretable.(*interpreter.ObservableInterpretable); ok {
p.observable = oi
}
return p, nil
}
// Eval implements the Program interface method.
func (p *prog) Eval(input any) (out ref.Val, det *EvalDetails, err error) {
// Configure error recovery for unexpected panics during evaluation. Note, the use of named
// return values makes it possible to modify the error response during the recovery
// function.
defer func() {
if r := recover(); r != nil {
switch t := r.(type) {
case interpreter.EvalCancelledError:
err = t
default:
err = fmt.Errorf("internal error: %v", r)
}
}
}()
// Build a hierarchical activation if there are default vars set.
var vars Activation
switch v := input.(type) {
case Activation:
vars = v
case map[string]any:
vars = activationPool.Setup(v)
defer activationPool.Put(vars)
default:
return nil, nil, fmt.Errorf("invalid input, wanted Activation or map[string]any, got: (%T)%v", input, input)
}
if p.defaultVars != nil {
vars = interpreter.NewHierarchicalActivation(p.defaultVars, vars)
}
if p.observable != nil {
det = &EvalDetails{}
out = p.observable.ObserveEval(vars, func(observed any) {
switch o := observed.(type) {
case interpreter.EvalState:
det.state = o
case *interpreter.CostTracker:
det.costTracker = o
}
})
} else {
out = p.interpretable.Eval(vars)
}
// The output of an internal Eval may have a value (`v`) that is a types.Err. This step
// translates the CEL value to a Go error response. This interface does not quite match the
// RPC signature which allows for multiple errors to be returned, but should be sufficient.
if types.IsError(out) {
err = out.(*types.Err)
}
return
}
// ContextEval implements the Program interface.
func (p *prog) ContextEval(ctx context.Context, input any) (ref.Val, *EvalDetails, error) {
if ctx == nil {
return nil, nil, fmt.Errorf("context can not be nil")
}
// Configure the input, making sure to wrap Activation inputs in the special ctxActivation which
// exposes the #interrupted variable and manages rate-limited checks of the ctx.Done() state.
var vars Activation
switch v := input.(type) {
case Activation:
vars = ctxActivationPool.Setup(v, ctx.Done(), p.interruptCheckFrequency)
defer ctxActivationPool.Put(vars)
case map[string]any:
rawVars := activationPool.Setup(v)
defer activationPool.Put(rawVars)
vars = ctxActivationPool.Setup(rawVars, ctx.Done(), p.interruptCheckFrequency)
defer ctxActivationPool.Put(vars)
default:
return nil, nil, fmt.Errorf("invalid input, wanted Activation or map[string]any, got: (%T)%v", input, input)
}
return p.Eval(vars)
}
type ctxEvalActivation struct {
parent Activation
interrupt <-chan struct{}
interruptCheckCount uint
interruptCheckFrequency uint
}
// ResolveName implements the Activation interface method, but adds a special #interrupted variable
// which is capable of testing whether a 'done' signal is provided from a context.Context channel.
func (a *ctxEvalActivation) ResolveName(name string) (any, bool) {
if name == "#interrupted" {
a.interruptCheckCount++
if a.interruptCheckCount%a.interruptCheckFrequency == 0 {
select {
case <-a.interrupt:
return true, true
default:
return nil, false
}
}
return nil, false
}
return a.parent.ResolveName(name)
}
func (a *ctxEvalActivation) Parent() Activation {
return a.parent
}
func (a *ctxEvalActivation) AsPartialActivation() (interpreter.PartialActivation, bool) {
pa, ok := a.parent.(interpreter.PartialActivation)
return pa, ok
}
func newCtxEvalActivationPool() *ctxEvalActivationPool {
return &ctxEvalActivationPool{
Pool: sync.Pool{
New: func() any {
return &ctxEvalActivation{}
},
},
}
}
type ctxEvalActivationPool struct {
sync.Pool
}
// Setup initializes a pooled Activation with the ability check for context.Context cancellation
func (p *ctxEvalActivationPool) Setup(vars Activation, done <-chan struct{}, interruptCheckRate uint) *ctxEvalActivation {
a := p.Pool.Get().(*ctxEvalActivation)
a.parent = vars
a.interrupt = done
a.interruptCheckCount = 0
a.interruptCheckFrequency = interruptCheckRate
return a
}
type evalActivation struct {
vars map[string]any
lazyVars map[string]any
}
// ResolveName looks up the value of the input variable name, if found.
//
// Lazy bindings may be supplied within the map-based input in either of the following forms:
// - func() any
// - func() ref.Val
//
// The lazy binding will only be invoked once per evaluation.
//
// Values which are not represented as ref.Val types on input may be adapted to a ref.Val using
// the types.Adapter configured in the environment.
func (a *evalActivation) ResolveName(name string) (any, bool) {
v, found := a.vars[name]
if !found {
return nil, false
}
switch obj := v.(type) {
case func() ref.Val:
if resolved, found := a.lazyVars[name]; found {
return resolved, true
}
lazy := obj()
a.lazyVars[name] = lazy
return lazy, true
case func() any:
if resolved, found := a.lazyVars[name]; found {
return resolved, true
}
lazy := obj()
a.lazyVars[name] = lazy
return lazy, true
default:
return obj, true
}
}
// Parent implements the Activation interface
func (a *evalActivation) Parent() Activation {
return nil
}
func newEvalActivationPool() *evalActivationPool {
return &evalActivationPool{
Pool: sync.Pool{
New: func() any {
return &evalActivation{lazyVars: make(map[string]any)}
},
},
}
}
type evalActivationPool struct {
sync.Pool
}
// Setup initializes a pooled Activation object with the map input.
func (p *evalActivationPool) Setup(vars map[string]any) *evalActivation {
a := p.Pool.Get().(*evalActivation)
a.vars = vars
return a
}
func (p *evalActivationPool) Put(value any) {
a := value.(*evalActivation)
for k := range a.lazyVars {
delete(a.lazyVars, k)
}
p.Pool.Put(a)
}
var (
// activationPool is an internally managed pool of Activation values that wrap map[string]any inputs
activationPool = newEvalActivationPool()
// ctxActivationPool is an internally managed pool of Activation values that expose a special #interrupted variable
ctxActivationPool = newCtxEvalActivationPool()
)
// Copyright 2025 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package cel
import (
_ "embed"
"sort"
"strings"
"text/template"
"github.com/google/cel-go/common"
"github.com/google/cel-go/common/operators"
"github.com/google/cel-go/common/overloads"
)
//go:embed templates/authoring.tmpl
var authoringPrompt string
// AuthoringPrompt creates a prompt template from a CEL environment for the purpose of AI-assisted authoring.
func AuthoringPrompt(env *Env) (*Prompt, error) {
funcMap := template.FuncMap{
"split": func(str string) []string { return strings.Split(str, "\n") },
}
tmpl := template.New("cel").Funcs(funcMap)
tmpl, err := tmpl.Parse(authoringPrompt)
if err != nil {
return nil, err
}
return &Prompt{
Persona: defaultPersona,
FormatRules: defaultFormatRules,
GeneralUsage: defaultGeneralUsage,
tmpl: tmpl,
env: env,
}, nil
}
// Prompt represents the core components of an LLM prompt based on a CEL environment.
//
// All fields of the prompt may be overwritten / modified with support for rendering the
// prompt to a human-readable string.
type Prompt struct {
// Persona indicates something about the kind of user making the request
Persona string
// FormatRules indicate how the LLM should generate its output
FormatRules string
// GeneralUsage specifies additional context on how CEL should be used.
GeneralUsage string
// tmpl is the text template base-configuration for rendering text.
tmpl *template.Template
// env reference used to collect variables, functions, and macros available to the prompt.
env *Env
}
type promptInst struct {
*Prompt
Variables []*common.Doc
Macros []*common.Doc
Functions []*common.Doc
UserPrompt string
}
// Render renders the user prompt with the associated context from the prompt template
// for use with LLM generators.
func (p *Prompt) Render(userPrompt string) string {
var buffer strings.Builder
vars := make([]*common.Doc, len(p.env.Variables()))
for i, v := range p.env.Variables() {
vars[i] = v.Documentation()
}
sort.SliceStable(vars, func(i, j int) bool {
return vars[i].Name < vars[j].Name
})
macs := make([]*common.Doc, len(p.env.Macros()))
for i, m := range p.env.Macros() {
macs[i] = m.(common.Documentor).Documentation()
}
funcs := make([]*common.Doc, 0, len(p.env.Functions()))
for _, f := range p.env.Functions() {
if _, hidden := hiddenFunctions[f.Name()]; hidden {
continue
}
funcs = append(funcs, f.Documentation())
}
sort.SliceStable(funcs, func(i, j int) bool {
return funcs[i].Name < funcs[j].Name
})
inst := &promptInst{
Prompt: p,
Variables: vars,
Macros: macs,
Functions: funcs,
UserPrompt: userPrompt}
p.tmpl.Execute(&buffer, inst)
return buffer.String()
}
const (
defaultPersona = `You are a software engineer with expertise in networking and application security
authoring boolean Common Expression Language (CEL) expressions to ensure firewall,
networking, authentication, and data access is only permitted when all conditions
are satisfied.`
defaultFormatRules = `Output your response as a CEL expression.
Write the expression with the comment on the first line and the expression on the
subsequent lines. Format the expression using 80-character line limits commonly
found in C++ or Java code.`
defaultGeneralUsage = `CEL supports Protocol Buffer and JSON types, as well as simple types and aggregate types.
Simple types include bool, bytes, double, int, string, and uint:
* double literals must always include a decimal point: 1.0, 3.5, -2.2
* uint literals must be positive values suffixed with a 'u': 42u
* byte literals are strings prefixed with a 'b': b'1235'
* string literals can use either single quotes or double quotes: 'hello', "world"
* string literals can also be treated as raw strings that do not require any
escaping within the string by using the 'R' prefix: R"""quote: "hi" """
Aggregate types include list and map:
* list literals consist of zero or more values between brackets: "['a', 'b', 'c']"
* map literal consist of colon-separated key-value pairs within braces: "{'key1': 1, 'key2': 2}"
* Only int, uint, string, and bool types are valid map keys.
* Maps containing HTTP headers must always use lower-cased string keys.
Comments start with two-forward slashes followed by text and a newline.`
)
var (
hiddenFunctions = map[string]bool{
overloads.DeprecatedIn: true,
operators.OldIn: true,
operators.OldNotStrictlyFalse: true,
operators.NotStrictlyFalse: true,
}
)
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package cel
import (
"fmt"
"reflect"
"regexp"
"github.com/google/cel-go/common/ast"
"github.com/google/cel-go/common/env"
"github.com/google/cel-go/common/overloads"
)
const (
durationValidatorName = "cel.validator.duration"
regexValidatorName = "cel.validator.matches"
timestampValidatorName = "cel.validator.timestamp"
homogeneousValidatorName = "cel.validator.homogeneous_literals"
nestingLimitValidatorName = "cel.validator.comprehension_nesting_limit"
// HomogeneousAggregateLiteralExemptFunctions is the ValidatorConfig key used to configure
// the set of function names which are exempt from homogeneous type checks. The expected type
// is a string list of function names.
//
// As an example, the `<string>.format([args])` call expects the input arguments list to be
// comprised of a variety of types which correspond to the types expected by the format control
// clauses; however, all other uses of a mixed element type list, would be unexpected.
HomogeneousAggregateLiteralExemptFunctions = homogeneousValidatorName + ".exempt"
)
var (
astValidatorFactories = map[string]ASTValidatorFactory{
nestingLimitValidatorName: func(val *env.Validator) (ASTValidator, error) {
if limit, found := val.ConfigValue("limit"); found {
// In case of protos, config value is of type by google.protobuf.Value, which numeric values are always a double.
if val, isDouble := limit.(float64); isDouble {
if val != float64(int64(val)) {
return nil, fmt.Errorf("invalid validator: %s, limit value is not a whole number: %v", nestingLimitValidatorName, limit)
}
return ValidateComprehensionNestingLimit(int(val)), nil
}
if val, isInt := limit.(int); isInt {
return ValidateComprehensionNestingLimit(val), nil
}
return nil, fmt.Errorf("invalid validator: %s unsupported limit type: %v", nestingLimitValidatorName, limit)
}
return nil, fmt.Errorf("invalid validator: %s missing limit", nestingLimitValidatorName)
},
durationValidatorName: func(*env.Validator) (ASTValidator, error) {
return ValidateDurationLiterals(), nil
},
regexValidatorName: func(*env.Validator) (ASTValidator, error) {
return ValidateRegexLiterals(), nil
},
timestampValidatorName: func(*env.Validator) (ASTValidator, error) {
return ValidateTimestampLiterals(), nil
},
homogeneousValidatorName: func(*env.Validator) (ASTValidator, error) {
return ValidateHomogeneousAggregateLiterals(), nil
},
}
)
// ASTValidatorFactory creates an ASTValidator as configured by the input map
type ASTValidatorFactory func(*env.Validator) (ASTValidator, error)
// ASTValidators configures a set of ASTValidator instances into the target environment.
//
// Validators are applied in the order in which the are specified and are treated as singletons.
// The same ASTValidator with a given name will not be applied more than once.
func ASTValidators(validators ...ASTValidator) EnvOption {
return func(e *Env) (*Env, error) {
for _, v := range validators {
if !e.HasValidator(v.Name()) {
e.validators = append(e.validators, v)
}
}
return e, nil
}
}
// ASTValidator defines a singleton interface for validating a type-checked Ast against an environment.
//
// Note: the Issues argument is mutable in the sense that it is intended to collect errors which will be
// reported to the caller.
type ASTValidator interface {
// Name returns the name of the validator. Names must be unique.
Name() string
// Validate validates a given Ast within an Environment and collects a set of potential issues.
//
// The ValidatorConfig is generated from the set of ASTValidatorConfigurer instances prior to
// the invocation of the Validate call. The expectation is that the validator configuration
// is created in sequence and immutable once provided to the Validate call.
//
// See individual validators for more information on their configuration keys and configuration
// properties.
Validate(*Env, ValidatorConfig, *ast.AST, *Issues)
}
// ConfigurableASTValidator supports conversion of an object to an `env.Validator` instance used for
// YAML serialization.
type ConfigurableASTValidator interface {
// ToConfig converts the internal configuration of an ASTValidator into an env.Validator instance
// which minimally must include the validator name, but may also include a map[string]any config
// object to be serialized to YAML. The string keys represent the configuration parameter name,
// and the any value must mirror the internally supported type associated with the config key.
//
// Note: only primitive CEL types are supported by CEL validators at this time.
ToConfig() *env.Validator
}
// ValidatorConfig provides an accessor method for querying validator configuration state.
type ValidatorConfig interface {
GetOrDefault(name string, value any) any
}
// MutableValidatorConfig provides mutation methods for querying and updating validator configuration
// settings.
type MutableValidatorConfig interface {
ValidatorConfig
Set(name string, value any) error
}
// ASTValidatorConfigurer indicates that this object, currently expected to be an ASTValidator,
// participates in validator configuration settings.
//
// This interface may be split from the expectation of being an ASTValidator instance in the future.
type ASTValidatorConfigurer interface {
Configure(MutableValidatorConfig) error
}
// validatorConfig implements the ValidatorConfig and MutableValidatorConfig interfaces.
type validatorConfig struct {
data map[string]any
}
// newValidatorConfig initializes the validator config with default values for core CEL validators.
func newValidatorConfig() *validatorConfig {
return &validatorConfig{
data: map[string]any{
HomogeneousAggregateLiteralExemptFunctions: []string{},
},
}
}
// GetOrDefault returns the configured value for the name, if present, else the input default value.
//
// Note, the type-agreement between the input default and configured value is not checked on read.
func (config *validatorConfig) GetOrDefault(name string, value any) any {
v, found := config.data[name]
if !found {
return value
}
return v
}
// Set configures a validator option with the given name and value.
//
// If the value had previously been set, the new value must have the same reflection type as the old one,
// or the call will error.
func (config *validatorConfig) Set(name string, value any) error {
v, found := config.data[name]
if found && reflect.TypeOf(v) != reflect.TypeOf(value) {
return fmt.Errorf("incompatible configuration type for %s, got %T, wanted %T", name, value, v)
}
config.data[name] = value
return nil
}
// ExtendedValidations collects a set of common AST validations which reduce the likelihood of runtime errors.
//
// - Validate duration and timestamp literals
// - Ensure regex strings are valid
// - Disable mixed type list and map literals
func ExtendedValidations() EnvOption {
return ASTValidators(
ValidateDurationLiterals(),
ValidateTimestampLiterals(),
ValidateRegexLiterals(),
ValidateHomogeneousAggregateLiterals(),
)
}
// ValidateDurationLiterals ensures that duration literal arguments are valid immediately after type-check.
func ValidateDurationLiterals() ASTValidator {
return newFormatValidator(overloads.TypeConvertDuration, 0, evalCall)
}
// ValidateTimestampLiterals ensures that timestamp literal arguments are valid immediately after type-check.
func ValidateTimestampLiterals() ASTValidator {
return newFormatValidator(overloads.TypeConvertTimestamp, 0, evalCall)
}
// ValidateRegexLiterals ensures that regex patterns are validated after type-check.
func ValidateRegexLiterals() ASTValidator {
return newFormatValidator(overloads.Matches, 0, compileRegex)
}
// ValidateHomogeneousAggregateLiterals checks that all list and map literals entries have the same types, i.e.
// no mixed list element types or mixed map key or map value types.
//
// Note: the string format call relies on a mixed element type list for ease of use, so this check skips all
// literals which occur within string format calls.
func ValidateHomogeneousAggregateLiterals() ASTValidator {
return homogeneousAggregateLiteralValidator{}
}
// ValidateComprehensionNestingLimit ensures that comprehension nesting does not exceed the specified limit.
//
// This validator can be useful for preventing arbitrarily nested comprehensions which can take high polynomial
// time to complete.
//
// Note, this limit does not apply to comprehensions with an empty iteration range, as these comprehensions have
// no actual looping cost. The cel.bind() utilizes the comprehension structure to perform local variable
// assignments and supplies an empty iteration range, so they won't count against the nesting limit either.
func ValidateComprehensionNestingLimit(limit int) ASTValidator {
return nestingLimitValidator{limit: limit}
}
type argChecker func(env *Env, call, arg ast.Expr) error
func newFormatValidator(funcName string, argNum int, check argChecker) formatValidator {
return formatValidator{
funcName: funcName,
check: check,
argNum: argNum,
}
}
type formatValidator struct {
funcName string
argNum int
check argChecker
}
// Name returns the unique name of this function format validator.
func (v formatValidator) Name() string {
return fmt.Sprintf("cel.validator.%s", v.funcName)
}
// ToConfig converts the ASTValidator to an env.Validator specifying the validator name.
func (v formatValidator) ToConfig() *env.Validator {
return env.NewValidator(v.Name())
}
// Validate searches the AST for uses of a given function name with a constant argument and performs a check
// on whether the argument is a valid literal value.
func (v formatValidator) Validate(e *Env, _ ValidatorConfig, a *ast.AST, iss *Issues) {
root := ast.NavigateAST(a)
funcCalls := ast.MatchDescendants(root, ast.FunctionMatcher(v.funcName))
for _, call := range funcCalls {
callArgs := call.AsCall().Args()
if len(callArgs) <= v.argNum {
continue
}
litArg := callArgs[v.argNum]
if litArg.Kind() != ast.LiteralKind {
continue
}
if err := v.check(e, call, litArg); err != nil {
iss.ReportErrorAtID(litArg.ID(), "invalid %s argument", v.funcName)
}
}
}
func evalCall(env *Env, call, arg ast.Expr) error {
ast := &Ast{impl: ast.NewAST(call, ast.NewSourceInfo(nil))}
prg, err := env.Program(ast)
if err != nil {
return err
}
_, _, err = prg.Eval(NoVars())
return err
}
func compileRegex(_ *Env, _, arg ast.Expr) error {
pattern := arg.AsLiteral().Value().(string)
_, err := regexp.Compile(pattern)
return err
}
type homogeneousAggregateLiteralValidator struct{}
// Name returns the unique name of the homogeneous type validator.
func (homogeneousAggregateLiteralValidator) Name() string {
return homogeneousValidatorName
}
// ToConfig converts the ASTValidator to an env.Validator specifying the validator name.
func (v homogeneousAggregateLiteralValidator) ToConfig() *env.Validator {
return env.NewValidator(v.Name())
}
// Validate validates that all lists and map literals have homogeneous types, i.e. don't contain dyn types.
//
// This validator makes an exception for list and map literals which occur at any level of nesting within
// string format calls.
func (v homogeneousAggregateLiteralValidator) Validate(_ *Env, c ValidatorConfig, a *ast.AST, iss *Issues) {
var exemptedFunctions []string
exemptedFunctions = c.GetOrDefault(HomogeneousAggregateLiteralExemptFunctions, exemptedFunctions).([]string)
root := ast.NavigateAST(a)
listExprs := ast.MatchDescendants(root, ast.KindMatcher(ast.ListKind))
for _, listExpr := range listExprs {
if inExemptFunction(listExpr, exemptedFunctions) {
continue
}
l := listExpr.AsList()
elements := l.Elements()
optIndices := l.OptionalIndices()
var elemType *Type
for i, e := range elements {
et := a.GetType(e.ID())
if isOptionalIndex(i, optIndices) {
et = et.Parameters()[0]
}
if elemType == nil {
elemType = et
continue
}
if !elemType.IsEquivalentType(et) {
v.typeMismatch(iss, e.ID(), elemType, et)
break
}
}
}
mapExprs := ast.MatchDescendants(root, ast.KindMatcher(ast.MapKind))
for _, mapExpr := range mapExprs {
if inExemptFunction(mapExpr, exemptedFunctions) {
continue
}
m := mapExpr.AsMap()
entries := m.Entries()
var keyType, valType *Type
for _, e := range entries {
mapEntry := e.AsMapEntry()
key, val := mapEntry.Key(), mapEntry.Value()
kt, vt := a.GetType(key.ID()), a.GetType(val.ID())
if mapEntry.IsOptional() {
vt = vt.Parameters()[0]
}
if keyType == nil && valType == nil {
keyType, valType = kt, vt
continue
}
if !keyType.IsEquivalentType(kt) {
v.typeMismatch(iss, key.ID(), keyType, kt)
}
if !valType.IsEquivalentType(vt) {
v.typeMismatch(iss, val.ID(), valType, vt)
}
}
}
}
func inExemptFunction(e ast.NavigableExpr, exemptFunctions []string) bool {
parent, found := e.Parent()
for found {
if parent.Kind() == ast.CallKind {
fnName := parent.AsCall().FunctionName()
for _, exempt := range exemptFunctions {
if exempt == fnName {
return true
}
}
}
parent, found = parent.Parent()
}
return false
}
func isOptionalIndex(i int, optIndices []int32) bool {
for _, optInd := range optIndices {
if i == int(optInd) {
return true
}
}
return false
}
func (homogeneousAggregateLiteralValidator) typeMismatch(iss *Issues, id int64, expected, actual *Type) {
iss.ReportErrorAtID(id, "expected type '%s' but found '%s'", FormatCELType(expected), FormatCELType(actual))
}
type nestingLimitValidator struct {
limit int
}
// Name returns the name of the nesting limit validator.
func (v nestingLimitValidator) Name() string {
return nestingLimitValidatorName
}
// ToConfig converts the ASTValidator to an env.Validator specifying the validator name and the nesting limit
// as an integer value: {"limit": int}
func (v nestingLimitValidator) ToConfig() *env.Validator {
return env.NewValidator(v.Name()).SetConfig(map[string]any{"limit": v.limit})
}
// Validate implements the ASTValidator interface method.
func (v nestingLimitValidator) Validate(e *Env, _ ValidatorConfig, a *ast.AST, iss *Issues) {
root := ast.NavigateAST(a)
comprehensions := ast.MatchDescendants(root, ast.KindMatcher(ast.ComprehensionKind))
if len(comprehensions) <= v.limit {
return
}
for _, comp := range comprehensions {
count := 0
e := comp
hasParent := true
for hasParent {
// When the expression is not a comprehension, continue to the next ancestor.
if e.Kind() != ast.ComprehensionKind {
e, hasParent = e.Parent()
continue
}
// When the comprehension has an empty range, continue to the next ancestor
// as this comprehension does not have any associated cost.
iterRange := e.AsComprehension().IterRange()
if iterRange.Kind() == ast.ListKind && iterRange.AsList().Size() == 0 {
e, hasParent = e.Parent()
continue
}
// Otherwise check the nesting limit.
count++
if count > v.limit {
iss.ReportErrorAtID(comp.ID(), "comprehension exceeds nesting limit")
break
}
e, hasParent = e.Parent()
}
}
}
// Copyright 2018 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Package checker defines functions to type-checked a parsed expression
// against a set of identifier and function declarations.
package checker
import (
"fmt"
"reflect"
"github.com/google/cel-go/common"
"github.com/google/cel-go/common/ast"
"github.com/google/cel-go/common/containers"
"github.com/google/cel-go/common/decls"
"github.com/google/cel-go/common/operators"
"github.com/google/cel-go/common/types"
"github.com/google/cel-go/common/types/ref"
)
type checker struct {
*ast.AST
ast.ExprFactory
env *Env
errors *typeErrors
mappings *mapping
freeTypeVarCounter int
}
// Check performs type checking, giving a typed AST.
//
// The input is a parsed AST and an env which encapsulates type binding of variables,
// declarations of built-in functions, descriptions of protocol buffers, and a registry for
// errors.
//
// Returns a type-checked AST, which might not be usable if there are errors in the error
// registry.
func Check(parsed *ast.AST, source common.Source, env *Env) (*ast.AST, *common.Errors) {
errs := common.NewErrors(source)
typeMap := make(map[int64]*types.Type)
refMap := make(map[int64]*ast.ReferenceInfo)
c := checker{
AST: ast.NewCheckedAST(parsed, typeMap, refMap),
ExprFactory: ast.NewExprFactory(),
env: env,
errors: &typeErrors{errs: errs},
mappings: newMapping(),
freeTypeVarCounter: 0,
}
c.check(c.Expr())
// Walk over the final type map substituting any type parameters either by their bound value
// or by DYN.
for id, t := range c.TypeMap() {
c.SetType(id, substitute(c.mappings, t, true))
}
return c.AST, errs
}
func (c *checker) check(e ast.Expr) {
if e == nil {
return
}
switch e.Kind() {
case ast.LiteralKind:
literal := ref.Val(e.AsLiteral())
switch literal.Type() {
case types.BoolType, types.BytesType, types.DoubleType, types.IntType,
types.NullType, types.StringType, types.UintType:
c.setType(e, literal.Type().(*types.Type))
default:
c.errors.unexpectedASTType(e.ID(), c.location(e), "literal", literal.Type().TypeName())
}
case ast.IdentKind:
c.checkIdent(e)
case ast.SelectKind:
c.checkSelect(e)
case ast.CallKind:
c.checkCall(e)
case ast.ListKind:
c.checkCreateList(e)
case ast.MapKind:
c.checkCreateMap(e)
case ast.StructKind:
c.checkCreateStruct(e)
case ast.ComprehensionKind:
c.checkComprehension(e)
default:
c.errors.unexpectedASTType(e.ID(), c.location(e), "unspecified", reflect.TypeOf(e).Name())
}
}
func (c *checker) checkIdent(e ast.Expr) {
identName := e.AsIdent()
// Check to see if the identifier is declared.
if ident := c.env.LookupIdent(identName); ident != nil {
c.setType(e, ident.Type())
c.setReference(e, ast.NewIdentReference(ident.Name(), ident.Value()))
// Overwrite the identifier with its fully qualified name.
e.SetKindCase(c.NewIdent(e.ID(), ident.Name()))
return
}
c.setType(e, types.ErrorType)
c.errors.undeclaredReference(e.ID(), c.location(e), c.env.container.Name(), identName)
}
func (c *checker) checkSelect(e ast.Expr) {
sel := e.AsSelect()
// Before traversing down the tree, try to interpret as qualified name.
qname, found := containers.ToQualifiedName(e)
if found {
ident := c.env.LookupIdent(qname)
if ident != nil {
// We don't check for a TestOnly expression here since the `found` result is
// always going to be false for TestOnly expressions.
// Rewrite the node to be a variable reference to the resolved fully-qualified
// variable name.
c.setType(e, ident.Type())
c.setReference(e, ast.NewIdentReference(ident.Name(), ident.Value()))
e.SetKindCase(c.NewIdent(e.ID(), ident.Name()))
return
}
}
resultType := c.checkSelectField(e, sel.Operand(), sel.FieldName(), false)
if sel.IsTestOnly() {
resultType = types.BoolType
}
c.setType(e, substitute(c.mappings, resultType, false))
}
func (c *checker) checkOptSelect(e ast.Expr) {
// Collect metadata related to the opt select call packaged by the parser.
call := e.AsCall()
if len(call.Args()) != 2 || call.IsMemberFunction() {
t := ""
if call.IsMemberFunction() {
t = " member call with"
}
c.errors.notAnOptionalFieldSelectionCall(e.ID(), c.location(e),
fmt.Sprintf(
"incorrect signature.%s argument count: %d", t, len(call.Args())))
return
}
operand := call.Args()[0]
field := call.Args()[1]
fieldName, isString := maybeUnwrapString(field)
if !isString {
c.errors.notAnOptionalFieldSelection(field.ID(), c.location(field), field)
return
}
// Perform type-checking using the field selection logic.
resultType := c.checkSelectField(e, operand, fieldName, true)
c.setType(e, substitute(c.mappings, resultType, false))
c.setReference(e, ast.NewFunctionReference("select_optional_field"))
}
func (c *checker) checkSelectField(e, operand ast.Expr, field string, optional bool) *types.Type {
// Interpret as field selection, first traversing down the operand.
c.check(operand)
operandType := substitute(c.mappings, c.getType(operand), false)
// If the target type is 'optional', unwrap it for the sake of this check.
targetType, isOpt := maybeUnwrapOptional(operandType)
// Assume error type by default as most types do not support field selection.
resultType := types.ErrorType
switch targetType.Kind() {
case types.MapKind:
// Maps yield their value type as the selection result type.
resultType = targetType.Parameters()[1]
case types.StructKind:
// Objects yield their field type declaration as the selection result type, but only if
// the field is defined.
messageType := targetType
if fieldType, found := c.lookupFieldType(e.ID(), messageType.TypeName(), field); found {
resultType = fieldType
}
case types.TypeParamKind:
// Set the operand type to DYN to prevent assignment to a potentially incorrect type
// at a later point in type-checking. The isAssignable call will update the type
// substitutions for the type param under the covers.
c.isAssignable(types.DynType, targetType)
// Also, set the result type to DYN.
resultType = types.DynType
default:
// Dynamic / error values are treated as DYN type. Errors are handled this way as well
// in order to allow forward progress on the check.
if !isDynOrError(targetType) {
c.errors.typeDoesNotSupportFieldSelection(e.ID(), c.location(e), targetType)
}
resultType = types.DynType
}
// If the target type was optional coming in, then the result must be optional going out.
if isOpt || optional {
return types.NewOptionalType(resultType)
}
return resultType
}
func (c *checker) checkCall(e ast.Expr) {
// Note: similar logic exists within the `interpreter/planner.go`. If making changes here
// please consider the impact on planner.go and consolidate implementations or mirror code
// as appropriate.
call := e.AsCall()
fnName := call.FunctionName()
if fnName == operators.OptSelect {
c.checkOptSelect(e)
return
}
args := call.Args()
// Traverse arguments.
for _, arg := range args {
c.check(arg)
}
// Regular static call with simple name.
if !call.IsMemberFunction() {
// Check for the existence of the function.
fn := c.env.LookupFunction(fnName)
if fn == nil {
c.errors.undeclaredReference(e.ID(), c.location(e), c.env.container.Name(), fnName)
c.setType(e, types.ErrorType)
return
}
// Overwrite the function name with its fully qualified resolved name.
e.SetKindCase(c.NewCall(e.ID(), fn.Name(), args...))
// Check to see whether the overload resolves.
c.resolveOverloadOrError(e, fn, nil, args)
return
}
// If a receiver 'target' is present, it may either be a receiver function, or a namespaced
// function, but not both. Given a.b.c() either a.b.c is a function or c is a function with
// target a.b.
//
// Check whether the target is a namespaced function name.
target := call.Target()
qualifiedPrefix, maybeQualified := containers.ToQualifiedName(target)
if maybeQualified {
maybeQualifiedName := qualifiedPrefix + "." + fnName
fn := c.env.LookupFunction(maybeQualifiedName)
if fn != nil {
// The function name is namespaced and so preserving the target operand would
// be an inaccurate representation of the desired evaluation behavior.
// Overwrite with fully-qualified resolved function name sans receiver target.
e.SetKindCase(c.NewCall(e.ID(), fn.Name(), args...))
c.resolveOverloadOrError(e, fn, nil, args)
return
}
}
// Regular instance call.
c.check(target)
fn := c.env.LookupFunction(fnName)
// Function found, attempt overload resolution.
if fn != nil {
c.resolveOverloadOrError(e, fn, target, args)
return
}
// Function name not declared, record error.
c.setType(e, types.ErrorType)
c.errors.undeclaredReference(e.ID(), c.location(e), c.env.container.Name(), fnName)
}
func (c *checker) resolveOverloadOrError(
e ast.Expr, fn *decls.FunctionDecl, target ast.Expr, args []ast.Expr) {
// Attempt to resolve the overload.
resolution := c.resolveOverload(e, fn, target, args)
// No such overload, error noted in the resolveOverload call, type recorded here.
if resolution == nil {
c.setType(e, types.ErrorType)
return
}
// Overload found.
c.setType(e, resolution.Type)
c.setReference(e, resolution.Reference)
}
func (c *checker) resolveOverload(
call ast.Expr, fn *decls.FunctionDecl, target ast.Expr, args []ast.Expr) *overloadResolution {
var argTypes []*types.Type
if target != nil {
argTypes = append(argTypes, c.getType(target))
}
for _, arg := range args {
argTypes = append(argTypes, c.getType(arg))
}
var resultType *types.Type
var checkedRef *ast.ReferenceInfo
for _, overload := range fn.OverloadDecls() {
// Determine whether the overload is currently considered.
if c.env.isOverloadDisabled(overload.ID()) {
continue
}
// Ensure the call style for the overload matches.
if (target == nil && overload.IsMemberFunction()) ||
(target != nil && !overload.IsMemberFunction()) {
// not a compatible call style.
continue
}
// Alternative type-checking behavior when the logical operators are compacted into
// variadic AST representations.
if fn.Name() == operators.LogicalAnd || fn.Name() == operators.LogicalOr {
checkedRef = ast.NewFunctionReference(overload.ID())
for i, argType := range argTypes {
if !c.isAssignable(argType, types.BoolType) {
c.errors.typeMismatch(
args[i].ID(),
c.locationByID(args[i].ID()),
types.BoolType,
argType)
resultType = types.ErrorType
}
}
if isError(resultType) {
return nil
}
return newResolution(checkedRef, types.BoolType)
}
overloadType := newFunctionType(overload.ResultType(), overload.ArgTypes()...)
typeParams := overload.TypeParams()
if len(typeParams) != 0 {
// Instantiate overload's type with fresh type variables.
substitutions := newMapping()
for _, typePar := range typeParams {
substitutions.add(types.NewTypeParamType(typePar), c.newTypeVar())
}
overloadType = substitute(substitutions, overloadType, false)
}
candidateArgTypes := overloadType.Parameters()[1:]
if c.isAssignableList(argTypes, candidateArgTypes) {
if checkedRef == nil {
checkedRef = ast.NewFunctionReference(overload.ID())
} else {
checkedRef.AddOverload(overload.ID())
}
// First matching overload, determines result type.
fnResultType := substitute(c.mappings, overloadType.Parameters()[0], false)
if resultType == nil {
resultType = fnResultType
} else if !isDyn(resultType) && !fnResultType.IsExactType(resultType) {
resultType = types.DynType
}
}
}
if resultType == nil {
for i, argType := range argTypes {
argTypes[i] = substitute(c.mappings, argType, true)
}
c.errors.noMatchingOverload(call.ID(), c.location(call), fn.Name(), argTypes, target != nil)
return nil
}
return newResolution(checkedRef, resultType)
}
func (c *checker) checkCreateList(e ast.Expr) {
create := e.AsList()
var elemsType *types.Type
optionalIndices := create.OptionalIndices()
optionals := make(map[int32]bool, len(optionalIndices))
for _, optInd := range optionalIndices {
optionals[optInd] = true
}
for i, e := range create.Elements() {
c.check(e)
elemType := c.getType(e)
if optionals[int32(i)] {
var isOptional bool
elemType, isOptional = maybeUnwrapOptional(elemType)
if !isOptional && !isDyn(elemType) {
c.errors.typeMismatch(e.ID(), c.location(e), types.NewOptionalType(elemType), elemType)
}
}
elemsType = c.joinTypes(e, elemsType, elemType)
}
if elemsType == nil {
// If the list is empty, assign free type var to elem type.
elemsType = c.newTypeVar()
}
c.setType(e, types.NewListType(elemsType))
}
func (c *checker) checkCreateMap(e ast.Expr) {
mapVal := e.AsMap()
var mapKeyType *types.Type
var mapValueType *types.Type
for _, e := range mapVal.Entries() {
entry := e.AsMapEntry()
key := entry.Key()
c.check(key)
mapKeyType = c.joinTypes(key, mapKeyType, c.getType(key))
val := entry.Value()
c.check(val)
valType := c.getType(val)
if entry.IsOptional() {
var isOptional bool
valType, isOptional = maybeUnwrapOptional(valType)
if !isOptional && !isDyn(valType) {
c.errors.typeMismatch(val.ID(), c.location(val), types.NewOptionalType(valType), valType)
}
}
mapValueType = c.joinTypes(val, mapValueType, valType)
}
if mapKeyType == nil {
// If the map is empty, assign free type variables to typeKey and value type.
mapKeyType = c.newTypeVar()
mapValueType = c.newTypeVar()
}
c.setType(e, types.NewMapType(mapKeyType, mapValueType))
}
func (c *checker) checkCreateStruct(e ast.Expr) {
msgVal := e.AsStruct()
// Determine the type of the message.
resultType := types.ErrorType
ident := c.env.LookupIdent(msgVal.TypeName())
if ident == nil {
c.errors.undeclaredReference(
e.ID(), c.location(e), c.env.container.Name(), msgVal.TypeName())
c.setType(e, types.ErrorType)
return
}
// Ensure the type name is fully qualified in the AST.
typeName := ident.Name()
if msgVal.TypeName() != typeName {
e.SetKindCase(c.NewStruct(e.ID(), typeName, msgVal.Fields()))
msgVal = e.AsStruct()
}
c.setReference(e, ast.NewIdentReference(typeName, nil))
identKind := ident.Type().Kind()
if identKind != types.ErrorKind {
if identKind != types.TypeKind {
c.errors.notAType(e.ID(), c.location(e), ident.Type().DeclaredTypeName())
} else {
resultType = ident.Type().Parameters()[0]
// Backwards compatibility test between well-known types and message types
// In this context, the type is being instantiated by its protobuf name which
// is not ideal or recommended, but some users expect this to work.
if isWellKnownType(resultType) {
typeName = getWellKnownTypeName(resultType)
} else if resultType.Kind() == types.StructKind {
typeName = resultType.DeclaredTypeName()
} else {
c.errors.notAMessageType(e.ID(), c.location(e), resultType.DeclaredTypeName())
resultType = types.ErrorType
}
}
}
c.setType(e, resultType)
// Check the field initializers.
for _, f := range msgVal.Fields() {
field := f.AsStructField()
fieldName := field.Name()
value := field.Value()
c.check(value)
fieldType := types.ErrorType
ft, found := c.lookupFieldType(f.ID(), typeName, fieldName)
if found {
fieldType = ft
}
valType := c.getType(value)
if field.IsOptional() {
var isOptional bool
valType, isOptional = maybeUnwrapOptional(valType)
if !isOptional && !isDyn(valType) {
c.errors.typeMismatch(value.ID(), c.location(value), types.NewOptionalType(valType), valType)
}
}
if !c.isAssignable(fieldType, valType) {
c.errors.fieldTypeMismatch(f.ID(), c.locationByID(f.ID()), fieldName, fieldType, valType)
}
}
}
func (c *checker) checkComprehension(e ast.Expr) {
comp := e.AsComprehension()
c.check(comp.IterRange())
c.check(comp.AccuInit())
rangeType := substitute(c.mappings, c.getType(comp.IterRange()), false)
// Create a scope for the comprehension since it has a local accumulation variable.
// This scope will contain the accumulation variable used to compute the result.
accuType := c.getType(comp.AccuInit())
c.env = c.env.enterScope()
c.env.AddIdents(decls.NewVariable(comp.AccuVar(), accuType))
var varType, var2Type *types.Type
switch rangeType.Kind() {
case types.ListKind:
// varType represents the list element type for one-variable comprehensions.
varType = rangeType.Parameters()[0]
if comp.HasIterVar2() {
// varType represents the list index (int) for two-variable comprehensions,
// and var2Type represents the list element type.
var2Type = varType
varType = types.IntType
}
case types.MapKind:
// varType represents the map entry key for all comprehension types.
varType = rangeType.Parameters()[0]
if comp.HasIterVar2() {
// var2Type represents the map entry value for two-variable comprehensions.
var2Type = rangeType.Parameters()[1]
}
case types.DynKind, types.ErrorKind, types.TypeParamKind:
// Set the range type to DYN to prevent assignment to a potentially incorrect type
// at a later point in type-checking. The isAssignable call will update the type
// substitutions for the type param under the covers.
c.isAssignable(types.DynType, rangeType)
// Set the range iteration variable to type DYN as well.
varType = types.DynType
if comp.HasIterVar2() {
var2Type = types.DynType
}
default:
c.errors.notAComprehensionRange(comp.IterRange().ID(), c.location(comp.IterRange()), rangeType)
varType = types.ErrorType
if comp.HasIterVar2() {
var2Type = types.ErrorType
}
}
// Create a block scope for the loop.
c.env = c.env.enterScope()
c.env.AddIdents(decls.NewVariable(comp.IterVar(), varType))
if comp.HasIterVar2() {
c.env.AddIdents(decls.NewVariable(comp.IterVar2(), var2Type))
}
// Check the variable references in the condition and step.
c.check(comp.LoopCondition())
c.assertType(comp.LoopCondition(), types.BoolType)
c.check(comp.LoopStep())
c.assertType(comp.LoopStep(), accuType)
// Exit the loop's block scope before checking the result.
c.env = c.env.exitScope()
c.check(comp.Result())
// Exit the comprehension scope.
c.env = c.env.exitScope()
c.setType(e, substitute(c.mappings, c.getType(comp.Result()), false))
}
// Checks compatibility of joined types, and returns the most general common type.
func (c *checker) joinTypes(e ast.Expr, previous, current *types.Type) *types.Type {
if previous == nil {
return current
}
if c.isAssignable(previous, current) {
return mostGeneral(previous, current)
}
if c.dynAggregateLiteralElementTypesEnabled() {
return types.DynType
}
c.errors.typeMismatch(e.ID(), c.location(e), previous, current)
return types.ErrorType
}
func (c *checker) dynAggregateLiteralElementTypesEnabled() bool {
return c.env.aggLitElemType == dynElementType
}
func (c *checker) newTypeVar() *types.Type {
id := c.freeTypeVarCounter
c.freeTypeVarCounter++
return types.NewTypeParamType(fmt.Sprintf("_var%d", id))
}
func (c *checker) isAssignable(t1, t2 *types.Type) bool {
subs := isAssignable(c.mappings, t1, t2)
if subs != nil {
c.mappings = subs
return true
}
return false
}
func (c *checker) isAssignableList(l1, l2 []*types.Type) bool {
subs := isAssignableList(c.mappings, l1, l2)
if subs != nil {
c.mappings = subs
return true
}
return false
}
func maybeUnwrapString(e ast.Expr) (string, bool) {
switch e.Kind() {
case ast.LiteralKind:
literal := e.AsLiteral()
switch v := literal.(type) {
case types.String:
return string(v), true
}
}
return "", false
}
func (c *checker) setType(e ast.Expr, t *types.Type) {
if old, found := c.TypeMap()[e.ID()]; found && !old.IsExactType(t) {
c.errors.incompatibleType(e.ID(), c.location(e), e, old, t)
return
}
c.SetType(e.ID(), t)
}
func (c *checker) getType(e ast.Expr) *types.Type {
return c.TypeMap()[e.ID()]
}
func (c *checker) setReference(e ast.Expr, r *ast.ReferenceInfo) {
if old, found := c.ReferenceMap()[e.ID()]; found && !old.Equals(r) {
c.errors.referenceRedefinition(e.ID(), c.location(e), e, old, r)
return
}
c.SetReference(e.ID(), r)
}
func (c *checker) assertType(e ast.Expr, t *types.Type) {
if !c.isAssignable(t, c.getType(e)) {
c.errors.typeMismatch(e.ID(), c.location(e), t, c.getType(e))
}
}
type overloadResolution struct {
Type *types.Type
Reference *ast.ReferenceInfo
}
func newResolution(r *ast.ReferenceInfo, t *types.Type) *overloadResolution {
return &overloadResolution{
Reference: r,
Type: t,
}
}
func (c *checker) location(e ast.Expr) common.Location {
return c.locationByID(e.ID())
}
func (c *checker) locationByID(id int64) common.Location {
return c.SourceInfo().GetStartLocation(id)
}
func (c *checker) lookupFieldType(exprID int64, structType, fieldName string) (*types.Type, bool) {
if _, found := c.env.provider.FindStructType(structType); !found {
// This should not happen, anyway, report an error.
c.errors.unexpectedFailedResolution(exprID, c.locationByID(exprID), structType)
return nil, false
}
if ft, found := c.env.provider.FindStructFieldType(structType, fieldName); found {
return ft.Type, found
}
c.errors.undefinedField(exprID, c.locationByID(exprID), fieldName)
return nil, false
}
func isWellKnownType(t *types.Type) bool {
switch t.Kind() {
case types.AnyKind, types.TimestampKind, types.DurationKind, types.DynKind, types.NullTypeKind:
return true
case types.BoolKind, types.BytesKind, types.DoubleKind, types.IntKind, types.StringKind, types.UintKind:
return t.IsAssignableType(types.NullType)
case types.ListKind:
return t.Parameters()[0] == types.DynType
case types.MapKind:
return t.Parameters()[0] == types.StringType && t.Parameters()[1] == types.DynType
}
return false
}
func getWellKnownTypeName(t *types.Type) string {
if name, found := wellKnownTypes[t.Kind()]; found {
return name
}
return ""
}
var (
wellKnownTypes = map[types.Kind]string{
types.AnyKind: "google.protobuf.Any",
types.BoolKind: "google.protobuf.BoolValue",
types.BytesKind: "google.protobuf.BytesValue",
types.DoubleKind: "google.protobuf.DoubleValue",
types.DurationKind: "google.protobuf.Duration",
types.DynKind: "google.protobuf.Value",
types.IntKind: "google.protobuf.Int64Value",
types.ListKind: "google.protobuf.ListValue",
types.NullTypeKind: "google.protobuf.NullValue",
types.MapKind: "google.protobuf.Struct",
types.StringKind: "google.protobuf.StringValue",
types.TimestampKind: "google.protobuf.Timestamp",
types.UintKind: "google.protobuf.UInt64Value",
}
)
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package checker
import (
"math"
"github.com/google/cel-go/common"
"github.com/google/cel-go/common/ast"
"github.com/google/cel-go/common/overloads"
"github.com/google/cel-go/common/types"
"github.com/google/cel-go/parser"
)
// WARNING: Any changes to cost calculations in this file require a corresponding change in interpreter/runtimecost.go
// CostEstimator estimates the sizes of variable length input data and the costs of functions.
type CostEstimator interface {
// EstimateSize returns a SizeEstimate for the given AstNode, or nil if the estimator has no
// estimate to provide.
//
// The size is equivalent to the result of the CEL `size()` function:
// * Number of unicode characters in a string
// * Number of bytes in a sequence
// * Number of map entries or number of list items.
//
// EstimateSize is only called for AstNodes where CEL does not know the size; EstimateSize is not
// called for values defined inline in CEL where the size is already obvious to CEL.
EstimateSize(element AstNode) *SizeEstimate
// EstimateCallCost returns the estimated cost of an invocation, or nil if the estimator has no
// estimate to provide.
EstimateCallCost(function, overloadID string, target *AstNode, args []AstNode) *CallEstimate
}
// CallEstimate includes a CostEstimate for the call, and an optional estimate of the result object size.
// The ResultSize should only be provided if the call results in a map, list, string or bytes.
type CallEstimate struct {
CostEstimate
ResultSize *SizeEstimate
}
// AstNode represents an AST node for the purpose of cost estimations.
type AstNode interface {
// Path returns a field path through the provided type declarations to the type of the AstNode, or nil if the AstNode does not
// represent type directly reachable from the provided type declarations.
// The first path element is a variable. All subsequent path elements are one of: field name, '@items', '@keys', '@values'.
Path() []string
// Type returns the deduced type of the AstNode.
Type() *types.Type
// Expr returns the expression of the AstNode.
Expr() ast.Expr
// ComputedSize returns a size estimate of the AstNode derived from information available in the CEL expression.
// For constants and inline list and map declarations, the exact size is returned. For concatenated list, strings
// and bytes, the size is derived from the size estimates of the operands. nil is returned if there is no
// computed size available.
ComputedSize() *SizeEstimate
}
type astNode struct {
path []string
t *types.Type
expr ast.Expr
derivedSize *SizeEstimate
}
func (e astNode) Path() []string {
return e.path
}
func (e astNode) Type() *types.Type {
return e.t
}
func (e astNode) Expr() ast.Expr {
return e.expr
}
func (e astNode) ComputedSize() *SizeEstimate {
return e.derivedSize
}
// SizeEstimate represents an estimated size of a variable length string, bytes, map or list.
type SizeEstimate struct {
Min, Max uint64
}
// UnknownSizeEstimate returns a size between 0 and max uint
func UnknownSizeEstimate() SizeEstimate {
return unknownSizeEstimate
}
// FixedSizeEstimate returns a size estimate with a fixed min and max range.
func FixedSizeEstimate(size uint64) SizeEstimate {
return SizeEstimate{Min: size, Max: size}
}
// Add adds to another SizeEstimate and returns the sum.
// If add would result in an uint64 overflow, the result is math.MaxUint64.
func (se SizeEstimate) Add(sizeEstimate SizeEstimate) SizeEstimate {
return SizeEstimate{
addUint64NoOverflow(se.Min, sizeEstimate.Min),
addUint64NoOverflow(se.Max, sizeEstimate.Max),
}
}
// Multiply multiplies by another SizeEstimate and returns the product.
// If multiply would result in an uint64 overflow, the result is math.MaxUint64.
func (se SizeEstimate) Multiply(sizeEstimate SizeEstimate) SizeEstimate {
return SizeEstimate{
multiplyUint64NoOverflow(se.Min, sizeEstimate.Min),
multiplyUint64NoOverflow(se.Max, sizeEstimate.Max),
}
}
// MultiplyByCostFactor multiplies a SizeEstimate by a cost factor and returns the CostEstimate with the
// nearest integer of the result, rounded up.
func (se SizeEstimate) MultiplyByCostFactor(costPerUnit float64) CostEstimate {
return CostEstimate{
multiplyByCostFactor(se.Min, costPerUnit),
multiplyByCostFactor(se.Max, costPerUnit),
}
}
// MultiplyByCost multiplies by the cost and returns the product.
// If multiply would result in an uint64 overflow, the result is math.MaxUint64.
func (se SizeEstimate) MultiplyByCost(cost CostEstimate) CostEstimate {
return CostEstimate{
multiplyUint64NoOverflow(se.Min, cost.Min),
multiplyUint64NoOverflow(se.Max, cost.Max),
}
}
// Union returns a SizeEstimate that encompasses both input the SizeEstimate.
func (se SizeEstimate) Union(size SizeEstimate) SizeEstimate {
result := se
if size.Min < result.Min {
result.Min = size.Min
}
if size.Max > result.Max {
result.Max = size.Max
}
return result
}
// CostEstimate represents an estimated cost range and provides add and multiply operations
// that do not overflow.
type CostEstimate struct {
Min, Max uint64
}
// UnknownCostEstimate returns a cost with an unknown impact.
func UnknownCostEstimate() CostEstimate {
return unknownCostEstimate
}
// FixedCostEstimate returns a cost with a fixed min and max range.
func FixedCostEstimate(cost uint64) CostEstimate {
return CostEstimate{Min: cost, Max: cost}
}
// Add adds the costs and returns the sum.
// If add would result in an uint64 overflow for the min or max, the value is set to math.MaxUint64.
func (ce CostEstimate) Add(cost CostEstimate) CostEstimate {
return CostEstimate{
Min: addUint64NoOverflow(ce.Min, cost.Min),
Max: addUint64NoOverflow(ce.Max, cost.Max),
}
}
// Multiply multiplies by the cost and returns the product.
// If multiply would result in an uint64 overflow, the result is math.MaxUint64.
func (ce CostEstimate) Multiply(cost CostEstimate) CostEstimate {
return CostEstimate{
Min: multiplyUint64NoOverflow(ce.Min, cost.Min),
Max: multiplyUint64NoOverflow(ce.Max, cost.Max),
}
}
// MultiplyByCostFactor multiplies a CostEstimate by a cost factor and returns the CostEstimate with the
// nearest integer of the result, rounded up.
func (ce CostEstimate) MultiplyByCostFactor(costPerUnit float64) CostEstimate {
return CostEstimate{
Min: multiplyByCostFactor(ce.Min, costPerUnit),
Max: multiplyByCostFactor(ce.Max, costPerUnit),
}
}
// Union returns a CostEstimate that encompasses both input the CostEstimates.
func (ce CostEstimate) Union(size CostEstimate) CostEstimate {
result := ce
if size.Min < result.Min {
result.Min = size.Min
}
if size.Max > result.Max {
result.Max = size.Max
}
return result
}
// addUint64NoOverflow adds non-negative ints. If the result is exceeds math.MaxUint64, math.MaxUint64
// is returned.
func addUint64NoOverflow(x, y uint64) uint64 {
if y > 0 && x > math.MaxUint64-y {
return math.MaxUint64
}
return x + y
}
// multiplyUint64NoOverflow multiplies non-negative ints. If the result is exceeds math.MaxUint64, math.MaxUint64
// is returned.
func multiplyUint64NoOverflow(x, y uint64) uint64 {
if y != 0 && x > math.MaxUint64/y {
return math.MaxUint64
}
return x * y
}
// multiplyByFactor multiplies an integer by a cost factor float and returns the nearest integer value, rounded up.
func multiplyByCostFactor(x uint64, y float64) uint64 {
xFloat := float64(x)
if xFloat > 0 && y > 0 && xFloat > math.MaxUint64/y {
return math.MaxUint64
}
ceil := math.Ceil(xFloat * y)
if ceil >= doubleTwoTo64 {
return math.MaxUint64
}
return uint64(ceil)
}
// CostOption configures flags which affect cost computations.
type CostOption func(*coster) error
// PresenceTestHasCost determines whether presence testing has a cost of one or zero.
//
// Defaults to presence test has a cost of one.
func PresenceTestHasCost(hasCost bool) CostOption {
return func(c *coster) error {
if hasCost {
c.presenceTestCost = selectAndIdentCost
return nil
}
c.presenceTestCost = FixedCostEstimate(0)
return nil
}
}
// FunctionEstimator provides a CallEstimate given the target and arguments for a specific function, overload pair.
type FunctionEstimator func(estimator CostEstimator, target *AstNode, args []AstNode) *CallEstimate
// OverloadCostEstimate binds a FunctionCoster to a specific function overload ID.
//
// When a OverloadCostEstimate is provided, it will override the cost calculation of the CostEstimator provided to
// the Cost() call.
func OverloadCostEstimate(overloadID string, functionCoster FunctionEstimator) CostOption {
return func(c *coster) error {
c.overloadEstimators[overloadID] = functionCoster
return nil
}
}
// Cost estimates the cost of the parsed and type checked CEL expression.
func Cost(checked *ast.AST, estimator CostEstimator, opts ...CostOption) (CostEstimate, error) {
c := &coster{
checkedAST: checked,
estimator: estimator,
overloadEstimators: map[string]FunctionEstimator{},
exprPaths: map[int64][]string{},
localVars: make(scopes),
computedSizes: map[int64]SizeEstimate{},
computedEntrySizes: map[int64]entrySizeEstimate{},
presenceTestCost: FixedCostEstimate(1),
}
for _, opt := range opts {
err := opt(c)
if err != nil {
return CostEstimate{}, err
}
}
return c.cost(checked.Expr()), nil
}
type coster struct {
// exprPaths maps from Expr Id to field path.
exprPaths map[int64][]string
// localVars tracks the local and iteration variables assigned during evaluation.
localVars scopes
// computedSizes tracks the computed sizes of call results.
computedSizes map[int64]SizeEstimate
// computedEntrySizes tracks the size of list and map entries
computedEntrySizes map[int64]entrySizeEstimate
checkedAST *ast.AST
estimator CostEstimator
overloadEstimators map[string]FunctionEstimator
// presenceTestCost will either be a zero or one based on whether has() macros count against cost computations.
presenceTestCost CostEstimate
}
// entrySizeEstimate captures the container kind and associated key/index and value SizeEstimate values.
//
// An entrySizeEstimate only exists if both the key/index and the value have SizeEstimate values, otherwise
// a nil entrySizeEstimate should be used.
type entrySizeEstimate struct {
containerKind types.Kind
key SizeEstimate
val SizeEstimate
}
// container returns the container kind (list or map) of the entry.
func (s *entrySizeEstimate) container() types.Kind {
if s == nil {
return types.UnknownKind
}
return s.containerKind
}
// keySize returns the SizeEstimate for the key if one exists.
func (s *entrySizeEstimate) keySize() *SizeEstimate {
if s == nil {
return nil
}
return &s.key
}
// valSize returns the SizeEstimate for the value if one exists.
func (s *entrySizeEstimate) valSize() *SizeEstimate {
if s == nil {
return nil
}
return &s.val
}
func (s *entrySizeEstimate) union(other *entrySizeEstimate) *entrySizeEstimate {
if s == nil || other == nil {
return nil
}
sk := s.key.Union(other.key)
sv := s.val.Union(other.val)
return &entrySizeEstimate{
containerKind: s.containerKind,
key: sk,
val: sv,
}
}
// localVar captures the local variable size and entrySize estimates if they exist for variables
type localVar struct {
exprID int64
path []string
size *SizeEstimate
entrySize *entrySizeEstimate
}
// scopes is a stack of variable name to integer id stack to handle scopes created by cel.bind() like macros
type scopes map[string][]*localVar
func (s scopes) push(varName string, expr ast.Expr, path []string, size *SizeEstimate, entrySize *entrySizeEstimate) {
s[varName] = append(s[varName], &localVar{
exprID: expr.ID(),
path: path,
size: size,
entrySize: entrySize,
})
}
func (s scopes) pop(varName string) {
varStack := s[varName]
s[varName] = varStack[:len(varStack)-1]
}
func (s scopes) peek(varName string) (*localVar, bool) {
varStack := s[varName]
if len(varStack) > 0 {
return varStack[len(varStack)-1], true
}
return nil, false
}
func (c *coster) pushIterKey(varName string, rangeExpr ast.Expr) {
entrySize := c.computeEntrySize(rangeExpr)
size := entrySize.keySize()
path := c.getPath(rangeExpr)
container := entrySize.container()
if container == types.UnknownKind {
container = c.getType(rangeExpr).Kind()
}
subpath := "@keys"
if container == types.ListKind {
subpath = "@indices"
}
c.localVars.push(varName, rangeExpr, append(path, subpath), size, nil)
}
func (c *coster) pushIterValue(varName string, rangeExpr ast.Expr) {
entrySize := c.computeEntrySize(rangeExpr)
size := entrySize.valSize()
path := c.getPath(rangeExpr)
container := entrySize.container()
if container == types.UnknownKind {
container = c.getType(rangeExpr).Kind()
}
subpath := "@values"
if container == types.ListKind {
subpath = "@items"
}
c.localVars.push(varName, rangeExpr, append(path, subpath), size, nil)
}
func (c *coster) pushIterSingle(varName string, rangeExpr ast.Expr) {
entrySize := c.computeEntrySize(rangeExpr)
size := entrySize.keySize()
subpath := "@keys"
container := entrySize.container()
if container == types.UnknownKind {
container = c.getType(rangeExpr).Kind()
}
if container == types.ListKind {
size = entrySize.valSize()
subpath = "@items"
}
path := c.getPath(rangeExpr)
c.localVars.push(varName, rangeExpr, append(path, subpath), size, nil)
}
func (c *coster) pushLocalVar(varName string, e ast.Expr) {
path := c.getPath(e)
// note: retrieve the entry size for the local variable based on the size of the binding expression
// since the binding expression could be a list or map, the entry size should also be propagated
entrySize := c.computeEntrySize(e)
c.localVars.push(varName, e, path, c.computeSize(e), entrySize)
}
func (c *coster) peekLocalVar(varName string) (*localVar, bool) {
return c.localVars.peek(varName)
}
func (c *coster) popLocalVar(varName string) {
c.localVars.pop(varName)
}
func (c *coster) cost(e ast.Expr) CostEstimate {
if e == nil {
return CostEstimate{}
}
var cost CostEstimate
switch e.Kind() {
case ast.LiteralKind:
cost = constCost
case ast.IdentKind:
cost = c.costIdent(e)
case ast.SelectKind:
cost = c.costSelect(e)
case ast.CallKind:
cost = c.costCall(e)
case ast.ListKind:
cost = c.costCreateList(e)
case ast.MapKind:
cost = c.costCreateMap(e)
case ast.StructKind:
cost = c.costCreateStruct(e)
case ast.ComprehensionKind:
if c.isBind(e) {
cost = c.costBind(e)
} else {
cost = c.costComprehension(e)
}
default:
return CostEstimate{}
}
return cost
}
func (c *coster) costIdent(e ast.Expr) CostEstimate {
identName := e.AsIdent()
// build and track the field path
if v, ok := c.peekLocalVar(identName); ok {
c.addPath(e, v.path)
} else {
c.addPath(e, []string{identName})
}
return selectAndIdentCost
}
func (c *coster) costSelect(e ast.Expr) CostEstimate {
sel := e.AsSelect()
var sum CostEstimate
if sel.IsTestOnly() {
// recurse, but do not add any cost
// this is equivalent to how evalTestOnly increments the runtime cost counter
// but does not add any additional cost for the qualifier, except here we do
// the reverse (ident adds cost)
sum = sum.Add(c.presenceTestCost)
sum = sum.Add(c.cost(sel.Operand()))
return sum
}
sum = sum.Add(c.cost(sel.Operand()))
targetType := c.getType(sel.Operand())
switch targetType.Kind() {
case types.MapKind, types.StructKind, types.TypeParamKind:
sum = sum.Add(selectAndIdentCost)
}
// build and track the field path
c.addPath(e, append(c.getPath(sel.Operand()), sel.FieldName()))
return sum
}
func (c *coster) costCall(e ast.Expr) CostEstimate {
// Dyn is just a way to disable type-checking, so return the cost of 1 with the cost of the argument
if dynEstimate := c.maybeUnwrapDynCall(e); dynEstimate != nil {
return *dynEstimate
}
// Continue estimating the cost of all other calls.
call := e.AsCall()
args := call.Args()
var sum CostEstimate
argTypes := make([]AstNode, len(args))
argCosts := make([]CostEstimate, len(args))
for i, arg := range args {
argCosts[i] = c.cost(arg)
argTypes[i] = c.newAstNode(arg)
}
overloadIDs := c.checkedAST.GetOverloadIDs(e.ID())
if len(overloadIDs) == 0 {
return CostEstimate{}
}
var targetType *AstNode
if call.IsMemberFunction() {
sum = sum.Add(c.cost(call.Target()))
var t AstNode = c.newAstNode(call.Target())
targetType = &t
}
// Pick a cost estimate range that covers all the overload cost estimation ranges
fnCost := CostEstimate{Min: uint64(math.MaxUint64), Max: 0}
var resultSize *SizeEstimate
for _, overload := range overloadIDs {
overloadCost := c.functionCost(e, call.FunctionName(), overload, targetType, argTypes, argCosts)
fnCost = fnCost.Union(overloadCost.CostEstimate)
if overloadCost.ResultSize != nil {
if resultSize == nil {
resultSize = overloadCost.ResultSize
} else {
size := resultSize.Union(*overloadCost.ResultSize)
resultSize = &size
}
}
// build and track the field path for index operations
switch overload {
case overloads.IndexList:
if len(args) > 0 {
// note: assigning resultSize here could be redundant with the path-based lookup later
resultSize = c.computeEntrySize(args[0]).valSize()
c.addPath(e, append(c.getPath(args[0]), "@items"))
}
case overloads.IndexMap:
if len(args) > 0 {
resultSize = c.computeEntrySize(args[0]).valSize()
c.addPath(e, append(c.getPath(args[0]), "@values"))
}
}
if resultSize == nil {
resultSize = c.computeSize(e)
}
}
c.setSize(e, resultSize)
return sum.Add(fnCost)
}
func (c *coster) maybeUnwrapDynCall(e ast.Expr) *CostEstimate {
call := e.AsCall()
if call.FunctionName() != "dyn" {
return nil
}
arg := call.Args()[0]
argCost := c.cost(arg)
c.copySizeEstimates(e, arg)
callCost := FixedCostEstimate(1).Add(argCost)
return &callCost
}
func (c *coster) costCreateList(e ast.Expr) CostEstimate {
create := e.AsList()
var sum CostEstimate
itemSize := SizeEstimate{Min: math.MaxUint64, Max: 0}
if create.Size() == 0 {
itemSize.Min = 0
}
for _, e := range create.Elements() {
sum = sum.Add(c.cost(e))
is := c.sizeOrUnknown(e)
itemSize = itemSize.Union(is)
}
c.setEntrySize(e, &entrySizeEstimate{containerKind: types.ListKind, key: FixedSizeEstimate(1), val: itemSize})
return sum.Add(createListBaseCost)
}
func (c *coster) costCreateMap(e ast.Expr) CostEstimate {
mapVal := e.AsMap()
var sum CostEstimate
keySize := SizeEstimate{Min: math.MaxUint64, Max: 0}
valSize := SizeEstimate{Min: math.MaxUint64, Max: 0}
if mapVal.Size() == 0 {
valSize.Min = 0
keySize.Min = 0
}
for _, ent := range mapVal.Entries() {
entry := ent.AsMapEntry()
sum = sum.Add(c.cost(entry.Key()))
sum = sum.Add(c.cost(entry.Value()))
// Compute the key size range
ks := c.sizeOrUnknown(entry.Key())
keySize = keySize.Union(ks)
// Compute the value size range
vs := c.sizeOrUnknown(entry.Value())
valSize = valSize.Union(vs)
}
c.setEntrySize(e, &entrySizeEstimate{containerKind: types.MapKind, key: keySize, val: valSize})
return sum.Add(createMapBaseCost)
}
func (c *coster) costCreateStruct(e ast.Expr) CostEstimate {
msgVal := e.AsStruct()
var sum CostEstimate
for _, ent := range msgVal.Fields() {
field := ent.AsStructField()
sum = sum.Add(c.cost(field.Value()))
}
return sum.Add(createMessageBaseCost)
}
func (c *coster) costComprehension(e ast.Expr) CostEstimate {
comp := e.AsComprehension()
var sum CostEstimate
sum = sum.Add(c.cost(comp.IterRange()))
sum = sum.Add(c.cost(comp.AccuInit()))
c.pushLocalVar(comp.AccuVar(), comp.AccuInit())
// Track the iterRange of each IterVar and AccuVar for field path construction
if comp.HasIterVar2() {
c.pushIterKey(comp.IterVar(), comp.IterRange())
c.pushIterValue(comp.IterVar2(), comp.IterRange())
} else {
c.pushIterSingle(comp.IterVar(), comp.IterRange())
}
// Determine the cost for each element in the loop
loopCost := c.cost(comp.LoopCondition())
stepCost := c.cost(comp.LoopStep())
// Clear the intermediate variable tracking.
c.popLocalVar(comp.IterVar())
if comp.HasIterVar2() {
c.popLocalVar(comp.IterVar2())
}
// Determine the result cost.
sum = sum.Add(c.cost(comp.Result()))
c.localVars.pop(comp.AccuVar())
// Estimate the cost of the loop.
rangeCnt := c.sizeOrUnknown(comp.IterRange())
rangeCost := rangeCnt.MultiplyByCost(stepCost.Add(loopCost))
sum = sum.Add(rangeCost)
switch k := comp.AccuInit().Kind(); k {
case ast.LiteralKind:
c.setSize(e, c.computeSize(comp.AccuInit()))
case ast.ListKind, ast.MapKind:
c.setSize(e, &rangeCnt)
// For a step which produces a container value, it will have an entry size associated
// with its expression id.
if stepEntrySize := c.computeEntrySize(comp.LoopStep()); stepEntrySize != nil {
c.setEntrySize(e, stepEntrySize)
break
}
}
return sum
}
func (c *coster) isBind(e ast.Expr) bool {
comp := e.AsComprehension()
iterRange := comp.IterRange()
loopCond := comp.LoopCondition()
return iterRange.Kind() == ast.ListKind && iterRange.AsList().Size() == 0 &&
loopCond.Kind() == ast.LiteralKind && loopCond.AsLiteral() == types.False &&
comp.AccuVar() != parser.AccumulatorName
}
func (c *coster) costBind(e ast.Expr) CostEstimate {
comp := e.AsComprehension()
var sum CostEstimate
// Binds are lazily initialized, so we retain the cost of an empty iteration range.
sum = sum.Add(c.cost(comp.IterRange()))
sum = sum.Add(c.cost(comp.AccuInit()))
c.pushLocalVar(comp.AccuVar(), comp.AccuInit())
sum = sum.Add(c.cost(comp.Result()))
c.popLocalVar(comp.AccuVar())
// Associate the bind output size with the result size.
c.copySizeEstimates(e, comp.Result())
return sum
}
func (c *coster) functionCost(e ast.Expr, function, overloadID string, target *AstNode, args []AstNode, argCosts []CostEstimate) CallEstimate {
argCostSum := func() CostEstimate {
var sum CostEstimate
for _, a := range argCosts {
sum = sum.Add(a)
}
return sum
}
if len(c.overloadEstimators) != 0 {
if estimator, found := c.overloadEstimators[overloadID]; found {
if est := estimator(c.estimator, target, args); est != nil {
callEst := *est
return CallEstimate{CostEstimate: callEst.Add(argCostSum()), ResultSize: est.ResultSize}
}
}
}
if est := c.estimator.EstimateCallCost(function, overloadID, target, args); est != nil {
callEst := *est
return CallEstimate{CostEstimate: callEst.Add(argCostSum()), ResultSize: est.ResultSize}
}
switch overloadID {
// O(n) functions
case overloads.ExtFormatString:
if target != nil {
// ResultSize not calculated because we can't bound the max size.
return CallEstimate{
CostEstimate: c.sizeOrUnknown(*target).MultiplyByCostFactor(common.StringTraversalCostFactor).Add(argCostSum())}
}
case overloads.StringToBytes:
if len(args) == 1 {
sz := c.sizeOrUnknown(args[0])
// ResultSize max is when each char converts to 4 bytes.
return CallEstimate{
CostEstimate: sz.MultiplyByCostFactor(common.StringTraversalCostFactor).Add(argCostSum()),
ResultSize: &SizeEstimate{Min: sz.Min, Max: sz.Max * 4}}
}
case overloads.BytesToString:
if len(args) == 1 {
sz := c.sizeOrUnknown(args[0])
// ResultSize min is when 4 bytes convert to 1 char.
return CallEstimate{
CostEstimate: sz.MultiplyByCostFactor(common.StringTraversalCostFactor).Add(argCostSum()),
ResultSize: &SizeEstimate{Min: sz.Min / 4, Max: sz.Max}}
}
case overloads.ExtQuoteString:
if len(args) == 1 {
sz := c.sizeOrUnknown(args[0])
// ResultSize max is when each char is escaped. 2 quote chars always added.
return CallEstimate{
CostEstimate: sz.MultiplyByCostFactor(common.StringTraversalCostFactor).Add(argCostSum()),
ResultSize: &SizeEstimate{Min: sz.Min + 2, Max: sz.Max*2 + 2}}
}
case overloads.StartsWithString, overloads.EndsWithString:
if len(args) == 1 {
return CallEstimate{CostEstimate: c.sizeOrUnknown(args[0]).MultiplyByCostFactor(common.StringTraversalCostFactor).Add(argCostSum())}
}
case overloads.InList:
// If a list is composed entirely of constant values this is O(1), but we don't account for that here.
// We just assume all list containment checks are O(n).
if len(args) == 2 {
return CallEstimate{CostEstimate: c.sizeOrUnknown(args[1]).MultiplyByCostFactor(1).Add(argCostSum())}
}
// O(nm) functions
case overloads.MatchesString:
// https://swtch.com/~rsc/regexp/regexp1.html applies to RE2 implementation supported by CEL
if target != nil && len(args) == 1 {
// Add one to string length for purposes of cost calculation to prevent product of string and regex to be 0
// in case where string is empty but regex is still expensive.
strCost := c.sizeOrUnknown(*target).Add(SizeEstimate{Min: 1, Max: 1}).MultiplyByCostFactor(common.StringTraversalCostFactor)
// We don't know how many expressions are in the regex, just the string length (a huge
// improvement here would be to somehow get a count the number of expressions in the regex or
// how many states are in the regex state machine and use that to measure regex cost).
// For now, we're making a guess that each expression in a regex is typically at least 4 chars
// in length.
regexCost := c.sizeOrUnknown(args[0]).MultiplyByCostFactor(common.RegexStringLengthCostFactor)
return CallEstimate{CostEstimate: strCost.Multiply(regexCost).Add(argCostSum())}
}
case overloads.ContainsString:
if target != nil && len(args) == 1 {
strCost := c.sizeOrUnknown(*target).MultiplyByCostFactor(common.StringTraversalCostFactor)
substrCost := c.sizeOrUnknown(args[0]).MultiplyByCostFactor(common.StringTraversalCostFactor)
return CallEstimate{CostEstimate: strCost.Multiply(substrCost).Add(argCostSum())}
}
case overloads.LogicalOr, overloads.LogicalAnd:
lhs := argCosts[0]
rhs := argCosts[1]
// min cost is min of LHS for short circuited && or ||
argCost := CostEstimate{Min: lhs.Min, Max: lhs.Add(rhs).Max}
return CallEstimate{CostEstimate: argCost}
case overloads.Conditional:
size := c.sizeOrUnknown(args[1]).Union(c.sizeOrUnknown(args[2]))
resultEntrySize := c.computeEntrySize(args[1].Expr()).union(c.computeEntrySize(args[2].Expr()))
c.setEntrySize(e, resultEntrySize)
conditionalCost := argCosts[0]
ifTrueCost := argCosts[1]
ifFalseCost := argCosts[2]
argCost := conditionalCost.Add(ifTrueCost.Union(ifFalseCost))
return CallEstimate{CostEstimate: argCost, ResultSize: &size}
case overloads.AddString, overloads.AddBytes, overloads.AddList:
if len(args) == 2 {
lhsSize := c.sizeOrUnknown(args[0])
rhsSize := c.sizeOrUnknown(args[1])
resultSize := lhsSize.Add(rhsSize)
rhsEntrySize := c.computeEntrySize(args[0].Expr())
lhsEntrySize := c.computeEntrySize(args[1].Expr())
resultEntrySize := rhsEntrySize.union(lhsEntrySize)
if resultEntrySize != nil {
c.setEntrySize(e, resultEntrySize)
}
switch overloadID {
case overloads.AddList:
// list concatenation is O(1), but we handle it here to track size
return CallEstimate{CostEstimate: FixedCostEstimate(1).Add(argCostSum()), ResultSize: &resultSize}
default:
return CallEstimate{CostEstimate: resultSize.MultiplyByCostFactor(common.StringTraversalCostFactor).Add(argCostSum()), ResultSize: &resultSize}
}
}
case overloads.LessString, overloads.GreaterString, overloads.LessEqualsString, overloads.GreaterEqualsString,
overloads.LessBytes, overloads.GreaterBytes, overloads.LessEqualsBytes, overloads.GreaterEqualsBytes,
overloads.Equals, overloads.NotEquals:
lhsCost := c.sizeOrUnknown(args[0])
rhsCost := c.sizeOrUnknown(args[1])
min := uint64(0)
smallestMax := lhsCost.Max
if rhsCost.Max < smallestMax {
smallestMax = rhsCost.Max
}
if smallestMax > 0 {
min = 1
}
// equality of 2 scalar values results in a cost of 1
return CallEstimate{
CostEstimate: CostEstimate{Min: min, Max: smallestMax}.MultiplyByCostFactor(common.StringTraversalCostFactor).Add(argCostSum()),
}
}
// O(1) functions
// See CostTracker.costCall for more details about O(1) cost calculations
// Benchmarks suggest that most of the other operations take +/- 50% of a base cost unit
// which on an Intel xeon 2.20GHz CPU is 50ns.
return CallEstimate{CostEstimate: FixedCostEstimate(1).Add(argCostSum())}
}
func (c *coster) getType(e ast.Expr) *types.Type {
return c.checkedAST.GetType(e.ID())
}
func (c *coster) getPath(e ast.Expr) []string {
if e.Kind() == ast.IdentKind {
if v, found := c.peekLocalVar(e.AsIdent()); found {
return v.path[:]
}
}
return c.exprPaths[e.ID()][:]
}
func (c *coster) addPath(e ast.Expr, path []string) {
c.exprPaths[e.ID()] = path
}
func isAccumulatorVar(name string) bool {
return name == parser.AccumulatorName || name == parser.HiddenAccumulatorName
}
func (c *coster) newAstNode(e ast.Expr) *astNode {
path := c.getPath(e)
if len(path) > 0 && isAccumulatorVar(path[0]) {
// only provide paths to root vars; omit accumulator vars
path = nil
}
return &astNode{
path: path,
t: c.getType(e),
expr: e,
derivedSize: c.computeSize(e)}
}
func (c *coster) setSize(e ast.Expr, size *SizeEstimate) {
if size == nil {
return
}
// Store the computed size with the expression
c.computedSizes[e.ID()] = *size
}
func (c *coster) sizeOrUnknown(node any) SizeEstimate {
switch v := node.(type) {
case ast.Expr:
if sz := c.computeSize(v); sz != nil {
return *sz
}
case AstNode:
if sz := v.ComputedSize(); sz != nil {
return *sz
}
}
return UnknownSizeEstimate()
}
func (c *coster) copySizeEstimates(dst, src ast.Expr) {
c.setSize(dst, c.computeSize(src))
c.setEntrySize(dst, c.computeEntrySize(src))
}
func (c *coster) computeSize(e ast.Expr) *SizeEstimate {
if size, ok := c.computedSizes[e.ID()]; ok {
return &size
}
if size := computeExprSize(e); size != nil {
return size
}
// Ensure size estimates are computed first as users may choose to override the costs that
// CEL would otherwise ascribe to the type.
node := astNode{expr: e, path: c.getPath(e), t: c.getType(e)}
if size := c.estimator.EstimateSize(node); size != nil {
// storing the computed size should reduce calls to EstimateSize()
c.computedSizes[e.ID()] = *size
return size
}
if size := computeTypeSize(c.getType(e)); size != nil {
return size
}
if e.Kind() == ast.IdentKind {
varName := e.AsIdent()
if v, ok := c.peekLocalVar(varName); ok && v.size != nil {
return v.size
}
}
return nil
}
func (c *coster) setEntrySize(e ast.Expr, size *entrySizeEstimate) {
if size == nil {
return
}
c.computedEntrySizes[e.ID()] = *size
}
func (c *coster) computeEntrySize(e ast.Expr) *entrySizeEstimate {
if sz, found := c.computedEntrySizes[e.ID()]; found {
return &sz
}
if e.Kind() == ast.IdentKind {
varName := e.AsIdent()
if v, ok := c.peekLocalVar(varName); ok && v.entrySize != nil {
return v.entrySize
}
}
return nil
}
func computeExprSize(expr ast.Expr) *SizeEstimate {
var v uint64
switch expr.Kind() {
case ast.LiteralKind:
switch ck := expr.AsLiteral().(type) {
case types.String:
// converting to runes here is an O(n) operation, but
// this is consistent with how size is computed at runtime,
// and how the language definition defines string size
v = uint64(len([]rune(ck)))
case types.Bytes:
v = uint64(len(ck))
case types.Bool, types.Double, types.Duration,
types.Int, types.Timestamp, types.Uint,
types.Null:
v = uint64(1)
default:
return nil
}
case ast.ListKind:
v = uint64(expr.AsList().Size())
case ast.MapKind:
v = uint64(expr.AsMap().Size())
default:
return nil
}
cost := FixedSizeEstimate(v)
return &cost
}
func computeTypeSize(t *types.Type) *SizeEstimate {
if isScalar(t) {
cost := FixedSizeEstimate(1)
return &cost
}
return nil
}
// isScalar returns true if the given type is known to be of a constant size at
// compile time. isScalar will return false for strings (they are variable-width)
// in addition to protobuf.Any and protobuf.Value (their size is not knowable at compile time).
func isScalar(t *types.Type) bool {
switch t.Kind() {
case types.BoolKind, types.DoubleKind, types.DurationKind, types.IntKind, types.TimestampKind, types.UintKind:
return true
case types.OpaqueKind:
if t.TypeName() == "optional_type" {
return isScalar(t.Parameters()[0])
}
}
return false
}
var (
doubleTwoTo64 = math.Ldexp(1.0, 64)
unknownSizeEstimate = SizeEstimate{Min: 0, Max: math.MaxUint64}
unknownCostEstimate = unknownSizeEstimate.MultiplyByCostFactor(1)
selectAndIdentCost = FixedCostEstimate(common.SelectAndIdentCost)
constCost = FixedCostEstimate(common.ConstCost)
createListBaseCost = FixedCostEstimate(common.ListCreateBaseCost)
createMapBaseCost = FixedCostEstimate(common.MapCreateBaseCost)
createMessageBaseCost = FixedCostEstimate(common.StructCreateBaseCost)
)
// Copyright 2018 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Package decls provides helpers for creating variable and function declarations.
package decls
import (
exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1"
emptypb "google.golang.org/protobuf/types/known/emptypb"
structpb "google.golang.org/protobuf/types/known/structpb"
)
var (
// Error type used to communicate issues during type-checking.
Error = &exprpb.Type{
TypeKind: &exprpb.Type_Error{
Error: &emptypb.Empty{}}}
// Dyn is a top-type used to represent any value.
Dyn = &exprpb.Type{
TypeKind: &exprpb.Type_Dyn{
Dyn: &emptypb.Empty{}}}
)
// Commonly used types.
var (
Bool = NewPrimitiveType(exprpb.Type_BOOL)
Bytes = NewPrimitiveType(exprpb.Type_BYTES)
Double = NewPrimitiveType(exprpb.Type_DOUBLE)
Int = NewPrimitiveType(exprpb.Type_INT64)
Null = &exprpb.Type{
TypeKind: &exprpb.Type_Null{
Null: structpb.NullValue_NULL_VALUE}}
String = NewPrimitiveType(exprpb.Type_STRING)
Uint = NewPrimitiveType(exprpb.Type_UINT64)
)
// Well-known types.
// TODO: Replace with an abstract type registry.
var (
Any = NewWellKnownType(exprpb.Type_ANY)
Duration = NewWellKnownType(exprpb.Type_DURATION)
Timestamp = NewWellKnownType(exprpb.Type_TIMESTAMP)
)
// NewAbstractType creates an abstract type declaration which references a proto
// message name and may also include type parameters.
func NewAbstractType(name string, paramTypes ...*exprpb.Type) *exprpb.Type {
return &exprpb.Type{
TypeKind: &exprpb.Type_AbstractType_{
AbstractType: &exprpb.Type_AbstractType{
Name: name,
ParameterTypes: paramTypes}}}
}
// NewOptionalType constructs an abstract type indicating that the parameterized type
// may be contained within the object.
func NewOptionalType(paramType *exprpb.Type) *exprpb.Type {
return NewAbstractType("optional_type", paramType)
}
// NewFunctionType creates a function invocation contract, typically only used
// by type-checking steps after overload resolution.
func NewFunctionType(resultType *exprpb.Type,
argTypes ...*exprpb.Type) *exprpb.Type {
return &exprpb.Type{
TypeKind: &exprpb.Type_Function{
Function: &exprpb.Type_FunctionType{
ResultType: resultType,
ArgTypes: argTypes}}}
}
// NewFunction creates a named function declaration with one or more overloads.
func NewFunction(name string,
overloads ...*exprpb.Decl_FunctionDecl_Overload) *exprpb.Decl {
return &exprpb.Decl{
Name: name,
DeclKind: &exprpb.Decl_Function{
Function: &exprpb.Decl_FunctionDecl{
Overloads: overloads}}}
}
// NewFunctionWithDoc creates a named function declaration with a description and one or more overloads.
func NewFunctionWithDoc(name, doc string,
overloads ...*exprpb.Decl_FunctionDecl_Overload) *exprpb.Decl {
return &exprpb.Decl{
Name: name,
DeclKind: &exprpb.Decl_Function{
Function: &exprpb.Decl_FunctionDecl{
// Doc: desc,
Overloads: overloads}}}
}
// NewIdent creates a named identifier declaration with an optional literal
// value.
//
// Literal values are typically only associated with enum identifiers.
//
// Deprecated: Use NewVar or NewConst instead.
func NewIdent(name string, t *exprpb.Type, v *exprpb.Constant) *exprpb.Decl {
return newIdent(name, t, v, "")
}
func newIdent(name string, t *exprpb.Type, v *exprpb.Constant, desc string) *exprpb.Decl {
return &exprpb.Decl{
Name: name,
DeclKind: &exprpb.Decl_Ident{
Ident: &exprpb.Decl_IdentDecl{
Type: t,
Value: v,
Doc: desc}}}
}
// NewConst creates a constant identifier with a CEL constant literal value.
func NewConst(name string, t *exprpb.Type, v *exprpb.Constant) *exprpb.Decl {
return newIdent(name, t, v, "")
}
// NewVar creates a variable identifier.
func NewVar(name string, t *exprpb.Type) *exprpb.Decl {
return newIdent(name, t, nil, "")
}
// NewVarWithDoc creates a variable identifier with a type and a description string.
func NewVarWithDoc(name string, t *exprpb.Type, desc string) *exprpb.Decl {
return newIdent(name, t, nil, desc)
}
// NewInstanceOverload creates a instance function overload contract.
// First element of argTypes is instance.
func NewInstanceOverload(id string, argTypes []*exprpb.Type, resultType *exprpb.Type) *exprpb.Decl_FunctionDecl_Overload {
return &exprpb.Decl_FunctionDecl_Overload{
OverloadId: id,
ResultType: resultType,
Params: argTypes,
IsInstanceFunction: true}
}
// NewListType generates a new list with elements of a certain type.
func NewListType(elem *exprpb.Type) *exprpb.Type {
return &exprpb.Type{
TypeKind: &exprpb.Type_ListType_{
ListType: &exprpb.Type_ListType{
ElemType: elem}}}
}
// NewMapType generates a new map with typed keys and values.
func NewMapType(key *exprpb.Type, value *exprpb.Type) *exprpb.Type {
return &exprpb.Type{
TypeKind: &exprpb.Type_MapType_{
MapType: &exprpb.Type_MapType{
KeyType: key,
ValueType: value}}}
}
// NewObjectType creates an object type for a qualified type name.
func NewObjectType(typeName string) *exprpb.Type {
return &exprpb.Type{
TypeKind: &exprpb.Type_MessageType{
MessageType: typeName}}
}
// NewOverload creates a function overload declaration which contains a unique
// overload id as well as the expected argument and result types. Overloads
// must be aggregated within a Function declaration.
func NewOverload(id string, argTypes []*exprpb.Type, resultType *exprpb.Type) *exprpb.Decl_FunctionDecl_Overload {
return &exprpb.Decl_FunctionDecl_Overload{
OverloadId: id,
ResultType: resultType,
Params: argTypes,
IsInstanceFunction: false}
}
// NewParameterizedInstanceOverload creates a parametric function instance overload type.
func NewParameterizedInstanceOverload(id string,
argTypes []*exprpb.Type,
resultType *exprpb.Type,
typeParams []string) *exprpb.Decl_FunctionDecl_Overload {
return &exprpb.Decl_FunctionDecl_Overload{
OverloadId: id,
ResultType: resultType,
Params: argTypes,
TypeParams: typeParams,
IsInstanceFunction: true}
}
// NewParameterizedOverload creates a parametric function overload type.
func NewParameterizedOverload(id string,
argTypes []*exprpb.Type,
resultType *exprpb.Type,
typeParams []string) *exprpb.Decl_FunctionDecl_Overload {
return &exprpb.Decl_FunctionDecl_Overload{
OverloadId: id,
ResultType: resultType,
Params: argTypes,
TypeParams: typeParams,
IsInstanceFunction: false}
}
// NewPrimitiveType creates a type for a primitive value. See the var declarations
// for Int, Uint, etc.
func NewPrimitiveType(primitive exprpb.Type_PrimitiveType) *exprpb.Type {
return &exprpb.Type{
TypeKind: &exprpb.Type_Primitive{
Primitive: primitive}}
}
// NewTypeType creates a new type designating a type.
func NewTypeType(nested *exprpb.Type) *exprpb.Type {
if nested == nil {
// must set the nested field for a valid oneof option
nested = &exprpb.Type{}
}
return &exprpb.Type{
TypeKind: &exprpb.Type_Type{
Type: nested}}
}
// NewTypeParamType creates a type corresponding to a named, contextual parameter.
func NewTypeParamType(name string) *exprpb.Type {
return &exprpb.Type{
TypeKind: &exprpb.Type_TypeParam{
TypeParam: name}}
}
// NewWellKnownType creates a type corresponding to a protobuf well-known type
// value.
func NewWellKnownType(wellKnown exprpb.Type_WellKnownType) *exprpb.Type {
return &exprpb.Type{
TypeKind: &exprpb.Type_WellKnown{
WellKnown: wellKnown}}
}
// NewWrapperType creates a wrapped primitive type instance. Wrapped types
// are roughly equivalent to a nullable, or optionally valued type.
func NewWrapperType(wrapped *exprpb.Type) *exprpb.Type {
primitive := wrapped.GetPrimitive()
if primitive == exprpb.Type_PRIMITIVE_TYPE_UNSPECIFIED {
// TODO: return an error
panic("Wrapped type must be a primitive")
}
return &exprpb.Type{TypeKind: &exprpb.Type_Wrapper{Wrapper: primitive}}
}
// Copyright 2018 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package checker
import (
"fmt"
"strings"
"github.com/google/cel-go/common/containers"
"github.com/google/cel-go/common/decls"
"github.com/google/cel-go/common/overloads"
"github.com/google/cel-go/common/types"
"github.com/google/cel-go/parser"
)
type aggregateLiteralElementType int
const (
dynElementType aggregateLiteralElementType = iota
homogenousElementType aggregateLiteralElementType = 1 << iota
)
var (
crossTypeNumericComparisonOverloads = map[string]struct{}{
// double <-> int | uint
overloads.LessDoubleInt64: {},
overloads.LessDoubleUint64: {},
overloads.LessEqualsDoubleInt64: {},
overloads.LessEqualsDoubleUint64: {},
overloads.GreaterDoubleInt64: {},
overloads.GreaterDoubleUint64: {},
overloads.GreaterEqualsDoubleInt64: {},
overloads.GreaterEqualsDoubleUint64: {},
// int <-> double | uint
overloads.LessInt64Double: {},
overloads.LessInt64Uint64: {},
overloads.LessEqualsInt64Double: {},
overloads.LessEqualsInt64Uint64: {},
overloads.GreaterInt64Double: {},
overloads.GreaterInt64Uint64: {},
overloads.GreaterEqualsInt64Double: {},
overloads.GreaterEqualsInt64Uint64: {},
// uint <-> double | int
overloads.LessUint64Double: {},
overloads.LessUint64Int64: {},
overloads.LessEqualsUint64Double: {},
overloads.LessEqualsUint64Int64: {},
overloads.GreaterUint64Double: {},
overloads.GreaterUint64Int64: {},
overloads.GreaterEqualsUint64Double: {},
overloads.GreaterEqualsUint64Int64: {},
}
)
// Env is the environment for type checking.
//
// The Env is comprised of a container, type provider, declarations, and other related objects
// which can be used to assist with type-checking.
type Env struct {
container *containers.Container
provider types.Provider
declarations *Scopes
aggLitElemType aggregateLiteralElementType
filteredOverloadIDs map[string]struct{}
}
// NewEnv returns a new *Env with the given parameters.
func NewEnv(container *containers.Container, provider types.Provider, opts ...Option) (*Env, error) {
declarations := newScopes()
declarations.Push()
envOptions := &options{}
for _, opt := range opts {
if err := opt(envOptions); err != nil {
return nil, err
}
}
aggLitElemType := dynElementType
if envOptions.homogeneousAggregateLiterals {
aggLitElemType = homogenousElementType
}
filteredOverloadIDs := crossTypeNumericComparisonOverloads
if envOptions.crossTypeNumericComparisons {
filteredOverloadIDs = make(map[string]struct{})
}
if envOptions.validatedDeclarations != nil {
declarations = envOptions.validatedDeclarations.Copy()
}
return &Env{
container: container,
provider: provider,
declarations: declarations,
aggLitElemType: aggLitElemType,
filteredOverloadIDs: filteredOverloadIDs,
}, nil
}
// AddIdents configures the checker with a list of variable declarations.
//
// If there are overlapping declarations, the method will error.
func (e *Env) AddIdents(declarations ...*decls.VariableDecl) error {
errMsgs := make([]errorMsg, 0)
for _, d := range declarations {
errMsgs = append(errMsgs, e.addIdent(d))
}
return formatError(errMsgs)
}
// AddFunctions configures the checker with a list of function declarations.
//
// If there are overlapping declarations, the method will error.
func (e *Env) AddFunctions(declarations ...*decls.FunctionDecl) error {
errMsgs := make([]errorMsg, 0)
for _, d := range declarations {
errMsgs = append(errMsgs, e.setFunction(d)...)
}
return formatError(errMsgs)
}
// LookupIdent returns a Decl proto for typeName as an identifier in the Env.
// Returns nil if no such identifier is found in the Env.
func (e *Env) LookupIdent(name string) *decls.VariableDecl {
for _, candidate := range e.container.ResolveCandidateNames(name) {
if ident := e.declarations.FindIdent(candidate); ident != nil {
return ident
}
// Next try to import the name as a reference to a message type. If found,
// the declaration is added to the outest (global) scope of the
// environment, so next time we can access it faster.
if t, found := e.provider.FindStructType(candidate); found {
decl := decls.NewVariable(candidate, t)
e.declarations.AddIdent(decl)
return decl
}
if i, found := e.provider.FindIdent(candidate); found {
if t, ok := i.(*types.Type); ok {
decl := decls.NewVariable(candidate, types.NewTypeTypeWithParam(t))
e.declarations.AddIdent(decl)
return decl
}
}
// Next try to import this as an enum value by splitting the name in a type prefix and
// the enum inside.
if enumValue := e.provider.EnumValue(candidate); enumValue.Type() != types.ErrType {
decl := decls.NewConstant(candidate, types.IntType, enumValue)
e.declarations.AddIdent(decl)
return decl
}
}
return nil
}
// LookupFunction returns a Decl proto for typeName as a function in env.
// Returns nil if no such function is found in env.
func (e *Env) LookupFunction(name string) *decls.FunctionDecl {
for _, candidate := range e.container.ResolveCandidateNames(name) {
if fn := e.declarations.FindFunction(candidate); fn != nil {
return fn
}
}
return nil
}
// setFunction adds the function Decl to the Env.
// Adds a function decl if one doesn't already exist, then adds all overloads from the Decl.
// If overload overlaps with an existing overload, adds to the errors in the Env instead.
func (e *Env) setFunction(fn *decls.FunctionDecl) []errorMsg {
errMsgs := make([]errorMsg, 0)
current := e.declarations.FindFunction(fn.Name())
if current != nil {
var err error
current, err = current.Merge(fn)
if err != nil {
return append(errMsgs, errorMsg(err.Error()))
}
} else {
current = fn
}
for _, overload := range current.OverloadDecls() {
for _, macro := range parser.AllMacros {
if macro.Function() == current.Name() &&
macro.IsReceiverStyle() == overload.IsMemberFunction() &&
macro.ArgCount() == len(overload.ArgTypes()) {
errMsgs = append(errMsgs, overlappingMacroError(current.Name(), macro.ArgCount()))
}
}
if len(errMsgs) > 0 {
return errMsgs
}
}
e.declarations.SetFunction(current)
return errMsgs
}
// addIdent adds the Decl to the declarations in the Env.
// Returns a non-empty errorMsg if the identifier is already declared in the scope.
func (e *Env) addIdent(decl *decls.VariableDecl) errorMsg {
current := e.declarations.FindIdentInScope(decl.Name())
if current != nil {
if current.DeclarationIsEquivalent(decl) {
return ""
}
return overlappingIdentifierError(decl.Name())
}
e.declarations.AddIdent(decl)
return ""
}
// isOverloadDisabled returns whether the overloadID is disabled in the current environment.
func (e *Env) isOverloadDisabled(overloadID string) bool {
_, found := e.filteredOverloadIDs[overloadID]
return found
}
// validatedDeclarations returns a reference to the validated variable and function declaration scope stack.
// must be copied before use.
func (e *Env) validatedDeclarations() *Scopes {
return e.declarations
}
// enterScope creates a new Env instance with a new innermost declaration scope.
func (e *Env) enterScope() *Env {
childDecls := e.declarations.Push()
return &Env{
declarations: childDecls,
container: e.container,
provider: e.provider,
aggLitElemType: e.aggLitElemType,
}
}
// exitScope creates a new Env instance with the nearest outer declaration scope.
func (e *Env) exitScope() *Env {
parentDecls := e.declarations.Pop()
return &Env{
declarations: parentDecls,
container: e.container,
provider: e.provider,
aggLitElemType: e.aggLitElemType,
}
}
// errorMsg is a type alias meant to represent error-based return values which
// may be accumulated into an error at a later point in execution.
type errorMsg string
func overlappingIdentifierError(name string) errorMsg {
return errorMsg(fmt.Sprintf("overlapping identifier for name '%s'", name))
}
func overlappingMacroError(name string, argCount int) errorMsg {
return errorMsg(fmt.Sprintf(
"overlapping macro for name '%s' with %d args", name, argCount))
}
func formatError(errMsgs []errorMsg) error {
errStrs := make([]string, 0)
if len(errMsgs) > 0 {
for i := 0; i < len(errMsgs); i++ {
if errMsgs[i] != "" {
errStrs = append(errStrs, string(errMsgs[i]))
}
}
}
if len(errStrs) > 0 {
return fmt.Errorf("%s", strings.Join(errStrs, "\n"))
}
return nil
}
// Copyright 2018 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package checker
import (
"github.com/google/cel-go/common"
"github.com/google/cel-go/common/ast"
"github.com/google/cel-go/common/types"
)
// typeErrors is a specialization of Errors.
type typeErrors struct {
errs *common.Errors
}
func (e *typeErrors) fieldTypeMismatch(id int64, l common.Location, name string, field, value *types.Type) {
e.errs.ReportErrorAtID(id, l, "expected type of field '%s' is '%s' but provided type is '%s'",
name, FormatCELType(field), FormatCELType(value))
}
func (e *typeErrors) incompatibleType(id int64, l common.Location, ex ast.Expr, prev, next *types.Type) {
e.errs.ReportErrorAtID(id, l,
"incompatible type already exists for expression: %v(%d) old:%v, new:%v", ex, ex.ID(), prev, next)
}
func (e *typeErrors) noMatchingOverload(id int64, l common.Location, name string, args []*types.Type, isInstance bool) {
signature := formatFunctionDeclType(nil, args, isInstance)
e.errs.ReportErrorAtID(id, l, "found no matching overload for '%s' applied to '%s'", name, signature)
}
func (e *typeErrors) notAComprehensionRange(id int64, l common.Location, t *types.Type) {
e.errs.ReportErrorAtID(id, l, "expression of type '%s' cannot be range of a comprehension (must be list, map, or dynamic)",
FormatCELType(t))
}
func (e *typeErrors) notAnOptionalFieldSelectionCall(id int64, l common.Location, err string) {
e.errs.ReportErrorAtID(id, l, "unsupported optional field selection: %s", err)
}
func (e *typeErrors) notAnOptionalFieldSelection(id int64, l common.Location, field ast.Expr) {
e.errs.ReportErrorAtID(id, l, "unsupported optional field selection: %v", field)
}
func (e *typeErrors) notAType(id int64, l common.Location, typeName string) {
e.errs.ReportErrorAtID(id, l, "'%s' is not a type", typeName)
}
func (e *typeErrors) notAMessageType(id int64, l common.Location, typeName string) {
e.errs.ReportErrorAtID(id, l, "'%s' is not a message type", typeName)
}
func (e *typeErrors) referenceRedefinition(id int64, l common.Location, ex ast.Expr, prev, next *ast.ReferenceInfo) {
e.errs.ReportErrorAtID(id, l,
"reference already exists for expression: %v(%d) old:%v, new:%v", ex, ex.ID(), prev, next)
}
func (e *typeErrors) typeDoesNotSupportFieldSelection(id int64, l common.Location, t *types.Type) {
e.errs.ReportErrorAtID(id, l, "type '%s' does not support field selection", FormatCELType(t))
}
func (e *typeErrors) typeMismatch(id int64, l common.Location, expected, actual *types.Type) {
e.errs.ReportErrorAtID(id, l, "expected type '%s' but found '%s'",
FormatCELType(expected), FormatCELType(actual))
}
func (e *typeErrors) undefinedField(id int64, l common.Location, field string) {
e.errs.ReportErrorAtID(id, l, "undefined field '%s'", field)
}
func (e *typeErrors) undeclaredReference(id int64, l common.Location, container string, name string) {
e.errs.ReportErrorAtID(id, l, "undeclared reference to '%s' (in container '%s')", name, container)
}
func (e *typeErrors) unexpectedFailedResolution(id int64, l common.Location, typeName string) {
e.errs.ReportErrorAtID(id, l, "unexpected failed resolution of '%s'", typeName)
}
func (e *typeErrors) unexpectedASTType(id int64, l common.Location, kind, typeName string) {
e.errs.ReportErrorAtID(id, l, "unexpected %s type: %v", kind, typeName)
}
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package checker
import (
"fmt"
"strings"
chkdecls "github.com/google/cel-go/checker/decls"
"github.com/google/cel-go/common/types"
exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1"
)
const (
kindUnknown = iota + 1
kindError
kindFunction
kindDyn
kindPrimitive
kindWellKnown
kindWrapper
kindNull
kindAbstract
kindType
kindList
kindMap
kindObject
kindTypeParam
)
// FormatCheckedType converts a type message into a string representation.
func FormatCheckedType(t *exprpb.Type) string {
switch kindOf(t) {
case kindDyn:
return "dyn"
case kindFunction:
return formatFunctionExprType(t.GetFunction().GetResultType(),
t.GetFunction().GetArgTypes(),
false)
case kindList:
return fmt.Sprintf("list(%s)", FormatCheckedType(t.GetListType().GetElemType()))
case kindObject:
return t.GetMessageType()
case kindMap:
return fmt.Sprintf("map(%s, %s)",
FormatCheckedType(t.GetMapType().GetKeyType()),
FormatCheckedType(t.GetMapType().GetValueType()))
case kindNull:
return "null"
case kindPrimitive:
switch t.GetPrimitive() {
case exprpb.Type_UINT64:
return "uint"
case exprpb.Type_INT64:
return "int"
}
return strings.Trim(strings.ToLower(t.GetPrimitive().String()), " ")
case kindType:
if t.GetType() == nil || t.GetType().GetTypeKind() == nil {
return "type"
}
return fmt.Sprintf("type(%s)", FormatCheckedType(t.GetType()))
case kindWellKnown:
switch t.GetWellKnown() {
case exprpb.Type_ANY:
return "any"
case exprpb.Type_DURATION:
return "duration"
case exprpb.Type_TIMESTAMP:
return "timestamp"
}
case kindWrapper:
return fmt.Sprintf("wrapper(%s)",
FormatCheckedType(chkdecls.NewPrimitiveType(t.GetWrapper())))
case kindError:
return "!error!"
case kindTypeParam:
return t.GetTypeParam()
case kindAbstract:
at := t.GetAbstractType()
params := at.GetParameterTypes()
paramStrs := make([]string, len(params))
for i, p := range params {
paramStrs[i] = FormatCheckedType(p)
}
return fmt.Sprintf("%s(%s)", at.GetName(), strings.Join(paramStrs, ", "))
}
return t.String()
}
type formatter func(any) string
// FormatCELType formats a types.Type value to a string representation.
//
// The type formatting is identical to FormatCheckedType.
func FormatCELType(t any) string {
dt := t.(*types.Type)
switch dt.Kind() {
case types.AnyKind:
return "any"
case types.DurationKind:
return "duration"
case types.ErrorKind:
return "!error!"
case types.NullTypeKind:
return "null"
case types.TimestampKind:
return "timestamp"
case types.TypeParamKind:
return dt.TypeName()
case types.OpaqueKind:
if dt.TypeName() == "function" {
// There is no explicit function type in the new types representation, so information like
// whether the function is a member function is absent.
return formatFunctionDeclType(dt.Parameters()[0], dt.Parameters()[1:], false)
}
case types.UnspecifiedKind:
return ""
}
if len(dt.Parameters()) == 0 {
return dt.DeclaredTypeName()
}
paramTypeNames := make([]string, 0, len(dt.Parameters()))
for _, p := range dt.Parameters() {
paramTypeNames = append(paramTypeNames, FormatCELType(p))
}
return fmt.Sprintf("%s(%s)", dt.TypeName(), strings.Join(paramTypeNames, ", "))
}
func formatExprType(t any) string {
if t == nil {
return ""
}
return FormatCheckedType(t.(*exprpb.Type))
}
func formatFunctionExprType(resultType *exprpb.Type, argTypes []*exprpb.Type, isInstance bool) string {
return formatFunctionInternal[*exprpb.Type](resultType, argTypes, isInstance, formatExprType)
}
func formatFunctionDeclType(resultType *types.Type, argTypes []*types.Type, isInstance bool) string {
return formatFunctionInternal[*types.Type](resultType, argTypes, isInstance, FormatCELType)
}
func formatFunctionInternal[T any](resultType T, argTypes []T, isInstance bool, format formatter) string {
result := ""
if isInstance {
target := argTypes[0]
argTypes = argTypes[1:]
result += format(target)
result += "."
}
result += "("
for i, arg := range argTypes {
if i > 0 {
result += ", "
}
result += format(arg)
}
result += ")"
rt := format(resultType)
if rt != "" {
result += " -> "
result += rt
}
return result
}
// kindOf returns the kind of the type as defined in the checked.proto.
func kindOf(t *exprpb.Type) int {
if t == nil || t.TypeKind == nil {
return kindUnknown
}
switch t.GetTypeKind().(type) {
case *exprpb.Type_Error:
return kindError
case *exprpb.Type_Function:
return kindFunction
case *exprpb.Type_Dyn:
return kindDyn
case *exprpb.Type_Primitive:
return kindPrimitive
case *exprpb.Type_WellKnown:
return kindWellKnown
case *exprpb.Type_Wrapper:
return kindWrapper
case *exprpb.Type_Null:
return kindNull
case *exprpb.Type_Type:
return kindType
case *exprpb.Type_ListType_:
return kindList
case *exprpb.Type_MapType_:
return kindMap
case *exprpb.Type_MessageType:
return kindObject
case *exprpb.Type_TypeParam:
return kindTypeParam
case *exprpb.Type_AbstractType_:
return kindAbstract
}
return kindUnknown
}
// Copyright 2018 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package checker
import (
"github.com/google/cel-go/common/types"
)
type mapping struct {
mapping map[string]*types.Type
}
func newMapping() *mapping {
return &mapping{
mapping: make(map[string]*types.Type),
}
}
func (m *mapping) add(from, to *types.Type) {
m.mapping[FormatCELType(from)] = to
}
func (m *mapping) find(from *types.Type) (*types.Type, bool) {
if r, found := m.mapping[FormatCELType(from)]; found {
return r, found
}
return nil, false
}
func (m *mapping) copy() *mapping {
c := newMapping()
for k, v := range m.mapping {
c.mapping[k] = v
}
return c
}
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package checker
type options struct {
crossTypeNumericComparisons bool
homogeneousAggregateLiterals bool
validatedDeclarations *Scopes
}
// Option is a functional option for configuring the type-checker
type Option func(*options) error
// CrossTypeNumericComparisons toggles type-checker support for numeric comparisons across type
// See https://github.com/google/cel-spec/wiki/proposal-210 for more details.
func CrossTypeNumericComparisons(enabled bool) Option {
return func(opts *options) error {
opts.crossTypeNumericComparisons = enabled
return nil
}
}
// ValidatedDeclarations provides a references to validated declarations which will be copied
// into new checker instances.
func ValidatedDeclarations(env *Env) Option {
return func(opts *options) error {
opts.validatedDeclarations = env.validatedDeclarations()
return nil
}
}
// Copyright 2018 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package checker
import (
"sort"
"github.com/google/cel-go/common/ast"
"github.com/google/cel-go/common/debug"
)
type semanticAdorner struct {
checked *ast.AST
}
var _ debug.Adorner = &semanticAdorner{}
func (a *semanticAdorner) GetMetadata(elem any) string {
result := ""
e, isExpr := elem.(ast.Expr)
if !isExpr {
return result
}
t := a.checked.TypeMap()[e.ID()]
if t != nil {
result += "~"
result += FormatCELType(t)
}
switch e.Kind() {
case ast.IdentKind,
ast.CallKind,
ast.ListKind,
ast.StructKind,
ast.SelectKind:
if ref, found := a.checked.ReferenceMap()[e.ID()]; found {
if len(ref.OverloadIDs) == 0 {
result += "^" + ref.Name
} else {
sort.Strings(ref.OverloadIDs)
for i, overload := range ref.OverloadIDs {
if i == 0 {
result += "^"
} else {
result += "|"
}
result += overload
}
}
}
}
return result
}
// Print returns a string representation of the Expr message,
// annotated with types from the CheckedExpr. The Expr must
// be a sub-expression embedded in the CheckedExpr.
func Print(e ast.Expr, checked *ast.AST) string {
a := &semanticAdorner{checked: checked}
return debug.ToAdornedDebugString(e, a)
}
// Copyright 2018 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package checker
import (
"github.com/google/cel-go/common/decls"
)
// Scopes represents nested Decl sets where the Scopes value contains a Groups containing all
// identifiers in scope and an optional parent representing outer scopes.
// Each Groups value is a mapping of names to Decls in the ident and function namespaces.
// Lookups are performed such that bindings in inner scopes shadow those in outer scopes.
type Scopes struct {
parent *Scopes
scopes *Group
}
// newScopes creates a new, empty Scopes.
// Some operations can't be safely performed until a Group is added with Push.
func newScopes() *Scopes {
return &Scopes{
scopes: newGroup(),
}
}
// Copy creates a copy of the current Scopes values, including a copy of its parent if non-nil.
func (s *Scopes) Copy() *Scopes {
cpy := newScopes()
if s == nil {
return cpy
}
if s.parent != nil {
cpy.parent = s.parent.Copy()
}
cpy.scopes = s.scopes.copy()
return cpy
}
// Push creates a new Scopes value which references the current Scope as its parent.
func (s *Scopes) Push() *Scopes {
return &Scopes{
parent: s,
scopes: newGroup(),
}
}
// Pop returns the parent Scopes value for the current scope, or the current scope if the parent
// is nil.
func (s *Scopes) Pop() *Scopes {
if s.parent != nil {
return s.parent
}
// TODO: Consider whether this should be an error / panic.
return s
}
// AddIdent adds the ident Decl in the current scope.
// Note: If the name collides with an existing identifier in the scope, the Decl is overwritten.
func (s *Scopes) AddIdent(decl *decls.VariableDecl) {
s.scopes.idents[decl.Name()] = decl
}
// FindIdent finds the first ident Decl with a matching name in Scopes, or nil if one cannot be
// found.
// Note: The search is performed from innermost to outermost.
func (s *Scopes) FindIdent(name string) *decls.VariableDecl {
if ident, found := s.scopes.idents[name]; found {
return ident
}
if s.parent != nil {
return s.parent.FindIdent(name)
}
return nil
}
// FindIdentInScope finds the first ident Decl with a matching name in the current Scopes value, or
// nil if one does not exist.
// Note: The search is only performed on the current scope and does not search outer scopes.
func (s *Scopes) FindIdentInScope(name string) *decls.VariableDecl {
if ident, found := s.scopes.idents[name]; found {
return ident
}
return nil
}
// SetFunction adds the function Decl to the current scope.
// Note: Any previous entry for a function in the current scope with the same name is overwritten.
func (s *Scopes) SetFunction(fn *decls.FunctionDecl) {
s.scopes.functions[fn.Name()] = fn
}
// FindFunction finds the first function Decl with a matching name in Scopes.
// The search is performed from innermost to outermost.
// Returns nil if no such function in Scopes.
func (s *Scopes) FindFunction(name string) *decls.FunctionDecl {
if fn, found := s.scopes.functions[name]; found {
return fn
}
if s.parent != nil {
return s.parent.FindFunction(name)
}
return nil
}
// Group is a set of Decls that is pushed on or popped off a Scopes as a unit.
// Contains separate namespaces for identifier and function Decls.
// (Should be named "Scope" perhaps?)
type Group struct {
idents map[string]*decls.VariableDecl
functions map[string]*decls.FunctionDecl
}
// copy creates a new Group instance with a shallow copy of the variables and functions.
// If callers need to mutate the exprpb.Decl definitions for a Function, they should copy-on-write.
func (g *Group) copy() *Group {
cpy := &Group{
idents: make(map[string]*decls.VariableDecl, len(g.idents)),
functions: make(map[string]*decls.FunctionDecl, len(g.functions)),
}
for n, id := range g.idents {
cpy.idents[n] = id
}
for n, fn := range g.functions {
cpy.functions[n] = fn
}
return cpy
}
// newGroup creates a new Group with empty maps for identifiers and functions.
func newGroup() *Group {
return &Group{
idents: make(map[string]*decls.VariableDecl),
functions: make(map[string]*decls.FunctionDecl),
}
}
// Copyright 2018 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package checker
import (
"github.com/google/cel-go/common/types"
)
// isDyn returns true if the input t is either type DYN or a well-known ANY message.
func isDyn(t *types.Type) bool {
// Note: object type values that are well-known and map to a DYN value in practice
// are sanitized prior to being added to the environment.
switch t.Kind() {
case types.DynKind, types.AnyKind:
return true
default:
return false
}
}
// isDynOrError returns true if the input is either an Error, DYN, or well-known ANY message.
func isDynOrError(t *types.Type) bool {
return isError(t) || isDyn(t)
}
func isError(t *types.Type) bool {
return t.Kind() == types.ErrorKind
}
func isOptional(t *types.Type) bool {
if t.Kind() == types.OpaqueKind {
return t.TypeName() == "optional_type"
}
return false
}
func maybeUnwrapOptional(t *types.Type) (*types.Type, bool) {
if isOptional(t) {
return t.Parameters()[0], true
}
return t, false
}
// isEqualOrLessSpecific checks whether one type is equal or less specific than the other one.
// A type is less specific if it matches the other type using the DYN type.
func isEqualOrLessSpecific(t1, t2 *types.Type) bool {
kind1, kind2 := t1.Kind(), t2.Kind()
// The first type is less specific.
if isDyn(t1) || kind1 == types.TypeParamKind {
return true
}
// The first type is not less specific.
if isDyn(t2) || kind2 == types.TypeParamKind {
return false
}
// Types must be of the same kind to be equal.
if kind1 != kind2 {
return false
}
// With limited exceptions for ANY and JSON values, the types must agree and be equivalent in
// order to return true.
switch kind1 {
case types.OpaqueKind:
if t1.TypeName() != t2.TypeName() ||
len(t1.Parameters()) != len(t2.Parameters()) {
return false
}
for i, p1 := range t1.Parameters() {
if !isEqualOrLessSpecific(p1, t2.Parameters()[i]) {
return false
}
}
return true
case types.ListKind:
return isEqualOrLessSpecific(t1.Parameters()[0], t2.Parameters()[0])
case types.MapKind:
return isEqualOrLessSpecific(t1.Parameters()[0], t2.Parameters()[0]) &&
isEqualOrLessSpecific(t1.Parameters()[1], t2.Parameters()[1])
case types.TypeKind:
return true
default:
return t1.IsExactType(t2)
}
}
// / internalIsAssignable returns true if t1 is assignable to t2.
func internalIsAssignable(m *mapping, t1, t2 *types.Type) bool {
// Process type parameters.
kind1, kind2 := t1.Kind(), t2.Kind()
if kind2 == types.TypeParamKind {
// If t2 is a valid type substitution for t1, return true.
valid, t2HasSub := isValidTypeSubstitution(m, t1, t2)
if valid {
return true
}
// If t2 is not a valid type sub for t1, and already has a known substitution return false
// since it is not possible for t1 to be a substitution for t2.
if !valid && t2HasSub {
return false
}
// Otherwise, fall through to check whether t1 is a possible substitution for t2.
}
if kind1 == types.TypeParamKind {
// Return whether t1 is a valid substitution for t2. If not, do no additional checks as the
// possible type substitutions have been searched in both directions.
valid, _ := isValidTypeSubstitution(m, t2, t1)
return valid
}
// Next check for wildcard types.
if isDynOrError(t1) || isDynOrError(t2) {
return true
}
// Preserve the nullness checks of the legacy type-checker.
if kind1 == types.NullTypeKind {
return internalIsAssignableNull(t2)
}
if kind2 == types.NullTypeKind {
return internalIsAssignableNull(t1)
}
// Test for when the types do not need to agree, but are more specific than dyn.
switch kind1 {
case types.BoolKind, types.BytesKind, types.DoubleKind, types.IntKind, types.StringKind, types.UintKind,
types.AnyKind, types.DurationKind, types.TimestampKind,
types.StructKind:
// Test whether t2 is assignable from t1. The order of this check won't usually matter;
// however, there may be cases where type capabilities are expanded beyond what is supported
// in the current common/types package. For example, an interface designation for a group of
// Struct types.
return t2.IsAssignableType(t1)
case types.TypeKind:
return kind2 == types.TypeKind
case types.OpaqueKind, types.ListKind, types.MapKind:
return t1.Kind() == t2.Kind() && t1.TypeName() == t2.TypeName() &&
internalIsAssignableList(m, t1.Parameters(), t2.Parameters())
default:
return false
}
}
// isValidTypeSubstitution returns whether t2 (or its type substitution) is a valid type
// substitution for t1, and whether t2 has a type substitution in mapping m.
//
// The type t2 is a valid substitution for t1 if any of the following statements is true
// - t2 has a type substitution (t2sub) equal to t1
// - t2 has a type substitution (t2sub) assignable to t1
// - t2 does not occur within t1.
func isValidTypeSubstitution(m *mapping, t1, t2 *types.Type) (valid, hasSub bool) {
// Early return if the t1 and t2 are the same instance.
kind1, kind2 := t1.Kind(), t2.Kind()
if kind1 == kind2 && t1.IsExactType(t2) {
return true, true
}
if t2Sub, found := m.find(t2); found {
// Early return if t1 and t2Sub are the same instance as otherwise the mapping
// might mark a type as being a subtitution for itself.
if kind1 == t2Sub.Kind() && t1.IsExactType(t2Sub) {
return true, true
}
// If the types are compatible, pick the more general type and return true
if internalIsAssignable(m, t1, t2Sub) {
t2New := mostGeneral(t1, t2Sub)
// only update the type reference map if the target type does not occur within it.
if notReferencedIn(m, t2, t2New) {
m.add(t2, t2New)
}
// acknowledge the type agreement, and that the substitution is already tracked.
return true, true
}
return false, true
}
if notReferencedIn(m, t2, t1) {
m.add(t2, t1)
return true, false
}
return false, false
}
// internalIsAssignableList returns true if the element types at each index in the list are
// assignable from l1[i] to l2[i]. The list lengths must also agree for the lists to be
// assignable.
func internalIsAssignableList(m *mapping, l1, l2 []*types.Type) bool {
if len(l1) != len(l2) {
return false
}
for i, t1 := range l1 {
if !internalIsAssignable(m, t1, l2[i]) {
return false
}
}
return true
}
// internalIsAssignableNull returns true if the type is nullable.
func internalIsAssignableNull(t *types.Type) bool {
return isLegacyNullable(t) || t.IsAssignableType(types.NullType)
}
// isLegacyNullable preserves the null-ness compatibility of the original type-checker implementation.
func isLegacyNullable(t *types.Type) bool {
switch t.Kind() {
case types.OpaqueKind, types.StructKind, types.AnyKind, types.DurationKind, types.TimestampKind:
return true
}
return false
}
// isAssignable returns an updated type substitution mapping if t1 is assignable to t2.
func isAssignable(m *mapping, t1, t2 *types.Type) *mapping {
mCopy := m.copy()
if internalIsAssignable(mCopy, t1, t2) {
return mCopy
}
return nil
}
// isAssignableList returns an updated type substitution mapping if l1 is assignable to l2.
func isAssignableList(m *mapping, l1, l2 []*types.Type) *mapping {
mCopy := m.copy()
if internalIsAssignableList(mCopy, l1, l2) {
return mCopy
}
return nil
}
// mostGeneral returns the more general of two types which are known to unify.
func mostGeneral(t1, t2 *types.Type) *types.Type {
if isEqualOrLessSpecific(t1, t2) {
return t1
}
return t2
}
// notReferencedIn checks whether the type doesn't appear directly or transitively within the other
// type. This is a standard requirement for type unification, commonly referred to as the "occurs
// check".
func notReferencedIn(m *mapping, t, withinType *types.Type) bool {
if t.IsExactType(withinType) {
return false
}
withinKind := withinType.Kind()
switch withinKind {
case types.TypeParamKind:
wtSub, found := m.find(withinType)
if !found {
return true
}
return notReferencedIn(m, t, wtSub)
case types.OpaqueKind, types.ListKind, types.MapKind, types.TypeKind:
for _, pt := range withinType.Parameters() {
if !notReferencedIn(m, t, pt) {
return false
}
}
return true
default:
return true
}
}
// substitute replaces all direct and indirect occurrences of bound type parameters. Unbound type
// parameters are replaced by DYN if typeParamToDyn is true.
func substitute(m *mapping, t *types.Type, typeParamToDyn bool) *types.Type {
if tSub, found := m.find(t); found {
return substitute(m, tSub, typeParamToDyn)
}
kind := t.Kind()
if typeParamToDyn && kind == types.TypeParamKind {
return types.DynType
}
switch kind {
case types.OpaqueKind:
return types.NewOpaqueType(t.TypeName(), substituteParams(m, t.Parameters(), typeParamToDyn)...)
case types.ListKind:
return types.NewListType(substitute(m, t.Parameters()[0], typeParamToDyn))
case types.MapKind:
return types.NewMapType(substitute(m, t.Parameters()[0], typeParamToDyn),
substitute(m, t.Parameters()[1], typeParamToDyn))
case types.TypeKind:
if len(t.Parameters()) > 0 {
tParam := t.Parameters()[0]
return types.NewTypeTypeWithParam(substitute(m, tParam, typeParamToDyn))
}
return t
default:
return t
}
}
func substituteParams(m *mapping, typeParams []*types.Type, typeParamToDyn bool) []*types.Type {
subParams := make([]*types.Type, len(typeParams))
for i, tp := range typeParams {
subParams[i] = substitute(m, tp, typeParamToDyn)
}
return subParams
}
func newFunctionType(resultType *types.Type, argTypes ...*types.Type) *types.Type {
return types.NewOpaqueType("function", append([]*types.Type{resultType}, argTypes...)...)
}
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Package ast declares data structures useful for parsed and checked abstract syntax trees
package ast
import (
"github.com/google/cel-go/common"
"github.com/google/cel-go/common/types"
"github.com/google/cel-go/common/types/ref"
)
// AST contains a protobuf expression and source info along with CEL-native type and reference information.
type AST struct {
expr Expr
sourceInfo *SourceInfo
typeMap map[int64]*types.Type
refMap map[int64]*ReferenceInfo
}
// Expr returns the root ast.Expr value in the AST.
func (a *AST) Expr() Expr {
if a == nil {
return nilExpr
}
return a.expr
}
// SourceInfo returns the source metadata associated with the parse / type-check passes.
func (a *AST) SourceInfo() *SourceInfo {
if a == nil {
return nil
}
return a.sourceInfo
}
// GetType returns the type for the expression at the given id, if one exists, else types.DynType.
func (a *AST) GetType(id int64) *types.Type {
if t, found := a.TypeMap()[id]; found {
return t
}
return types.DynType
}
// SetType sets the type of the expression node at the given id.
func (a *AST) SetType(id int64, t *types.Type) {
if a == nil {
return
}
a.typeMap[id] = t
}
// TypeMap returns the map of expression ids to type-checked types.
//
// If the AST is not type-checked, the map will be empty.
func (a *AST) TypeMap() map[int64]*types.Type {
if a == nil {
return map[int64]*types.Type{}
}
return a.typeMap
}
// GetOverloadIDs returns the set of overload function names for a given expression id.
//
// If the expression id is not a function call, or the AST is not type-checked, the result will be empty.
func (a *AST) GetOverloadIDs(id int64) []string {
if ref, found := a.ReferenceMap()[id]; found {
return ref.OverloadIDs
}
return []string{}
}
// ReferenceMap returns the map of expression id to identifier, constant, and function references.
func (a *AST) ReferenceMap() map[int64]*ReferenceInfo {
if a == nil {
return map[int64]*ReferenceInfo{}
}
return a.refMap
}
// SetReference adds a reference to the checked AST type map.
func (a *AST) SetReference(id int64, r *ReferenceInfo) {
if a == nil {
return
}
a.refMap[id] = r
}
// IsChecked returns whether the AST is type-checked.
func (a *AST) IsChecked() bool {
return a != nil && len(a.TypeMap()) > 0
}
// NewAST creates a base AST instance with an ast.Expr and ast.SourceInfo value.
func NewAST(e Expr, sourceInfo *SourceInfo) *AST {
if e == nil {
e = nilExpr
}
return &AST{
expr: e,
sourceInfo: sourceInfo,
typeMap: make(map[int64]*types.Type),
refMap: make(map[int64]*ReferenceInfo),
}
}
// NewCheckedAST wraps an parsed AST and augments it with type and reference metadata.
func NewCheckedAST(parsed *AST, typeMap map[int64]*types.Type, refMap map[int64]*ReferenceInfo) *AST {
return &AST{
expr: parsed.Expr(),
sourceInfo: parsed.SourceInfo(),
typeMap: typeMap,
refMap: refMap,
}
}
// Copy creates a deep copy of the Expr and SourceInfo values in the input AST.
//
// Copies of the Expr value are generated using an internal default ExprFactory.
func Copy(a *AST) *AST {
if a == nil {
return nil
}
e := defaultFactory.CopyExpr(a.expr)
if !a.IsChecked() {
return NewAST(e, CopySourceInfo(a.SourceInfo()))
}
typesCopy := make(map[int64]*types.Type, len(a.typeMap))
for id, t := range a.typeMap {
typesCopy[id] = t
}
refsCopy := make(map[int64]*ReferenceInfo, len(a.refMap))
for id, r := range a.refMap {
refsCopy[id] = r
}
return NewCheckedAST(NewAST(e, CopySourceInfo(a.SourceInfo())), typesCopy, refsCopy)
}
// MaxID returns the upper-bound, non-inclusive, of ids present within the AST's Expr value.
func MaxID(a *AST) int64 {
visitor := &maxIDVisitor{maxID: 1}
PostOrderVisit(a.Expr(), visitor)
for id, call := range a.SourceInfo().MacroCalls() {
PostOrderVisit(call, visitor)
if id > visitor.maxID {
visitor.maxID = id + 1
}
}
return visitor.maxID + 1
}
// Heights computes the heights of all AST expressions and returns a map from expression id to height.
func Heights(a *AST) map[int64]int {
visitor := make(heightVisitor)
PostOrderVisit(a.Expr(), visitor)
return visitor
}
// NewSourceInfo creates a simple SourceInfo object from an input common.Source value.
func NewSourceInfo(src common.Source) *SourceInfo {
var lineOffsets []int32
var desc string
baseLine := int32(0)
baseCol := int32(0)
if src != nil {
desc = src.Description()
lineOffsets = src.LineOffsets()
// Determine whether the source metadata should be computed relative
// to a base line and column value. This can be determined by requesting
// the location for offset 0 from the source object.
if loc, found := src.OffsetLocation(0); found {
baseLine = int32(loc.Line()) - 1
baseCol = int32(loc.Column())
}
}
return &SourceInfo{
desc: desc,
lines: lineOffsets,
baseLine: baseLine,
baseCol: baseCol,
offsetRanges: make(map[int64]OffsetRange),
macroCalls: make(map[int64]Expr),
}
}
// CopySourceInfo creates a deep copy of the MacroCalls within the input SourceInfo.
//
// Copies of macro Expr values are generated using an internal default ExprFactory.
func CopySourceInfo(info *SourceInfo) *SourceInfo {
if info == nil {
return nil
}
rangesCopy := make(map[int64]OffsetRange, len(info.offsetRanges))
for id, off := range info.offsetRanges {
rangesCopy[id] = off
}
callsCopy := make(map[int64]Expr, len(info.macroCalls))
for id, call := range info.macroCalls {
callsCopy[id] = defaultFactory.CopyExpr(call)
}
return &SourceInfo{
syntax: info.syntax,
desc: info.desc,
lines: info.lines,
baseLine: info.baseLine,
baseCol: info.baseCol,
offsetRanges: rangesCopy,
macroCalls: callsCopy,
}
}
// SourceInfo records basic information about the expression as a textual input and
// as a parsed expression value.
type SourceInfo struct {
syntax string
desc string
lines []int32
baseLine int32
baseCol int32
offsetRanges map[int64]OffsetRange
macroCalls map[int64]Expr
}
// SyntaxVersion returns the syntax version associated with the text expression.
func (s *SourceInfo) SyntaxVersion() string {
if s == nil {
return ""
}
return s.syntax
}
// Description provides information about where the expression came from.
func (s *SourceInfo) Description() string {
if s == nil {
return ""
}
return s.desc
}
// LineOffsets returns a list of the 0-based character offsets in the input text where newlines appear.
func (s *SourceInfo) LineOffsets() []int32 {
if s == nil {
return []int32{}
}
return s.lines
}
// MacroCalls returns a map of expression id to ast.Expr value where the id represents the expression
// node where the macro was inserted into the AST, and the ast.Expr value represents the original call
// signature which was replaced.
func (s *SourceInfo) MacroCalls() map[int64]Expr {
if s == nil {
return map[int64]Expr{}
}
return s.macroCalls
}
// GetMacroCall returns the original ast.Expr value for the given expression if it was generated via
// a macro replacement.
//
// Note, parsing options must be enabled to track macro calls before this method will return a value.
func (s *SourceInfo) GetMacroCall(id int64) (Expr, bool) {
e, found := s.MacroCalls()[id]
return e, found
}
// SetMacroCall records a macro call at a specific location.
func (s *SourceInfo) SetMacroCall(id int64, e Expr) {
if s != nil {
s.macroCalls[id] = e
}
}
// ClearMacroCall removes the macro call at the given expression id.
func (s *SourceInfo) ClearMacroCall(id int64) {
if s != nil {
delete(s.macroCalls, id)
}
}
// OffsetRanges returns a map of expression id to OffsetRange values where the range indicates either:
// the start and end position in the input stream where the expression occurs, or the start position
// only. If the range only captures start position, the stop position of the range will be equal to
// the start.
func (s *SourceInfo) OffsetRanges() map[int64]OffsetRange {
if s == nil {
return map[int64]OffsetRange{}
}
return s.offsetRanges
}
// GetOffsetRange retrieves an OffsetRange for the given expression id if one exists.
func (s *SourceInfo) GetOffsetRange(id int64) (OffsetRange, bool) {
if s == nil {
return OffsetRange{}, false
}
o, found := s.offsetRanges[id]
return o, found
}
// SetOffsetRange sets the OffsetRange for the given expression id.
func (s *SourceInfo) SetOffsetRange(id int64, o OffsetRange) {
if s == nil {
return
}
s.offsetRanges[id] = o
}
// ClearOffsetRange removes the OffsetRange for the given expression id.
func (s *SourceInfo) ClearOffsetRange(id int64) {
if s != nil {
delete(s.offsetRanges, id)
}
}
// GetStartLocation calculates the human-readable 1-based line and 0-based column of the first character
// of the expression node at the id.
func (s *SourceInfo) GetStartLocation(id int64) common.Location {
if o, found := s.GetOffsetRange(id); found {
return s.GetLocationByOffset(o.Start)
}
return common.NoLocation
}
// GetStopLocation calculates the human-readable 1-based line and 0-based column of the last character for
// the expression node at the given id.
//
// If the SourceInfo was generated from a serialized protobuf representation, the stop location will
// be identical to the start location for the expression.
func (s *SourceInfo) GetStopLocation(id int64) common.Location {
if o, found := s.GetOffsetRange(id); found {
return s.GetLocationByOffset(o.Stop)
}
return common.NoLocation
}
// GetLocationByOffset returns the line and column information for a given character offset.
func (s *SourceInfo) GetLocationByOffset(offset int32) common.Location {
line := 1
col := int(offset)
for _, lineOffset := range s.LineOffsets() {
if lineOffset > offset {
break
}
line++
col = int(offset - lineOffset)
}
return common.NewLocation(line, col)
}
// ComputeOffset calculates the 0-based character offset from a 1-based line and 0-based column.
func (s *SourceInfo) ComputeOffset(line, col int32) int32 {
if s != nil {
line = s.baseLine + line
col = s.baseCol + col
}
if line == 1 {
return col
}
if line < 1 || line > int32(len(s.LineOffsets())) {
return -1
}
offset := s.LineOffsets()[line-2]
return offset + col
}
// OffsetRange captures the start and stop positions of a section of text in the input expression.
type OffsetRange struct {
Start int32
Stop int32
}
// ReferenceInfo contains a CEL native representation of an identifier reference which may refer to
// either a qualified identifier name, a set of overload ids, or a constant value from an enum.
type ReferenceInfo struct {
Name string
OverloadIDs []string
Value ref.Val
}
// NewIdentReference creates a ReferenceInfo instance for an identifier with an optional constant value.
func NewIdentReference(name string, value ref.Val) *ReferenceInfo {
return &ReferenceInfo{Name: name, Value: value}
}
// NewFunctionReference creates a ReferenceInfo instance for a set of function overloads.
func NewFunctionReference(overloads ...string) *ReferenceInfo {
info := &ReferenceInfo{}
for _, id := range overloads {
info.AddOverload(id)
}
return info
}
// AddOverload appends a function overload ID to the ReferenceInfo.
func (r *ReferenceInfo) AddOverload(overloadID string) {
for _, id := range r.OverloadIDs {
if id == overloadID {
return
}
}
r.OverloadIDs = append(r.OverloadIDs, overloadID)
}
// Equals returns whether two references are identical to each other.
func (r *ReferenceInfo) Equals(other *ReferenceInfo) bool {
if r.Name != other.Name {
return false
}
if len(r.OverloadIDs) != len(other.OverloadIDs) {
return false
}
if len(r.OverloadIDs) != 0 {
overloadMap := make(map[string]struct{}, len(r.OverloadIDs))
for _, id := range r.OverloadIDs {
overloadMap[id] = struct{}{}
}
for _, id := range other.OverloadIDs {
_, found := overloadMap[id]
if !found {
return false
}
}
}
if r.Value == nil && other.Value == nil {
return true
}
if r.Value == nil && other.Value != nil ||
r.Value != nil && other.Value == nil ||
r.Value.Equal(other.Value) != types.True {
return false
}
return true
}
type maxIDVisitor struct {
maxID int64
*baseVisitor
}
// VisitExpr updates the max identifier if the incoming expression id is greater than previously observed.
func (v *maxIDVisitor) VisitExpr(e Expr) {
if v.maxID < e.ID() {
v.maxID = e.ID()
}
}
// VisitEntryExpr updates the max identifier if the incoming entry id is greater than previously observed.
func (v *maxIDVisitor) VisitEntryExpr(e EntryExpr) {
if v.maxID < e.ID() {
v.maxID = e.ID()
}
}
type heightVisitor map[int64]int
// VisitExpr computes the height of a given node as the max height of its children plus one.
//
// Identifiers and literals are treated as having a height of zero.
func (hv heightVisitor) VisitExpr(e Expr) {
// default includes IdentKind, LiteralKind
hv[e.ID()] = 0
switch e.Kind() {
case SelectKind:
hv[e.ID()] = 1 + hv[e.AsSelect().Operand().ID()]
case CallKind:
c := e.AsCall()
height := hv.maxHeight(c.Args()...)
if c.IsMemberFunction() {
tHeight := hv[c.Target().ID()]
if tHeight > height {
height = tHeight
}
}
hv[e.ID()] = 1 + height
case ListKind:
l := e.AsList()
hv[e.ID()] = 1 + hv.maxHeight(l.Elements()...)
case MapKind:
m := e.AsMap()
hv[e.ID()] = 1 + hv.maxEntryHeight(m.Entries()...)
case StructKind:
s := e.AsStruct()
hv[e.ID()] = 1 + hv.maxEntryHeight(s.Fields()...)
case ComprehensionKind:
comp := e.AsComprehension()
hv[e.ID()] = 1 + hv.maxHeight(comp.IterRange(), comp.AccuInit(), comp.LoopCondition(), comp.LoopStep(), comp.Result())
}
}
// VisitEntryExpr computes the max height of a map or struct entry and associates the height with the entry id.
func (hv heightVisitor) VisitEntryExpr(e EntryExpr) {
hv[e.ID()] = 0
switch e.Kind() {
case MapEntryKind:
me := e.AsMapEntry()
hv[e.ID()] = hv.maxHeight(me.Value(), me.Key())
case StructFieldKind:
sf := e.AsStructField()
hv[e.ID()] = hv[sf.Value().ID()]
}
}
func (hv heightVisitor) maxHeight(exprs ...Expr) int {
max := 0
for _, e := range exprs {
h := hv[e.ID()]
if h > max {
max = h
}
}
return max
}
func (hv heightVisitor) maxEntryHeight(entries ...EntryExpr) int {
max := 0
for _, e := range entries {
h := hv[e.ID()]
if h > max {
max = h
}
}
return max
}
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package ast
import (
"fmt"
"google.golang.org/protobuf/proto"
"github.com/google/cel-go/common/types"
"github.com/google/cel-go/common/types/ref"
celpb "cel.dev/expr"
exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1"
structpb "google.golang.org/protobuf/types/known/structpb"
)
// ToProto converts an AST to a CheckedExpr protobouf.
func ToProto(ast *AST) (*exprpb.CheckedExpr, error) {
refMap := make(map[int64]*exprpb.Reference, len(ast.ReferenceMap()))
for id, ref := range ast.ReferenceMap() {
r, err := ReferenceInfoToProto(ref)
if err != nil {
return nil, err
}
refMap[id] = r
}
typeMap := make(map[int64]*exprpb.Type, len(ast.TypeMap()))
for id, typ := range ast.TypeMap() {
t, err := types.TypeToExprType(typ)
if err != nil {
return nil, err
}
typeMap[id] = t
}
e, err := ExprToProto(ast.Expr())
if err != nil {
return nil, err
}
info, err := SourceInfoToProto(ast.SourceInfo())
if err != nil {
return nil, err
}
return &exprpb.CheckedExpr{
Expr: e,
SourceInfo: info,
ReferenceMap: refMap,
TypeMap: typeMap,
}, nil
}
// ToAST converts a CheckedExpr protobuf to an AST instance.
func ToAST(checked *exprpb.CheckedExpr) (*AST, error) {
refMap := make(map[int64]*ReferenceInfo, len(checked.GetReferenceMap()))
for id, ref := range checked.GetReferenceMap() {
r, err := ProtoToReferenceInfo(ref)
if err != nil {
return nil, err
}
refMap[id] = r
}
typeMap := make(map[int64]*types.Type, len(checked.GetTypeMap()))
for id, typ := range checked.GetTypeMap() {
t, err := types.ExprTypeToType(typ)
if err != nil {
return nil, err
}
typeMap[id] = t
}
info, err := ProtoToSourceInfo(checked.GetSourceInfo())
if err != nil {
return nil, err
}
root, err := ProtoToExpr(checked.GetExpr())
if err != nil {
return nil, err
}
ast := NewCheckedAST(NewAST(root, info), typeMap, refMap)
return ast, nil
}
// ProtoToExpr converts a protobuf Expr value to an ast.Expr value.
func ProtoToExpr(e *exprpb.Expr) (Expr, error) {
factory := NewExprFactory()
return exprInternal(factory, e)
}
// ProtoToEntryExpr converts a protobuf struct/map entry to an ast.EntryExpr
func ProtoToEntryExpr(e *exprpb.Expr_CreateStruct_Entry) (EntryExpr, error) {
factory := NewExprFactory()
switch e.GetKeyKind().(type) {
case *exprpb.Expr_CreateStruct_Entry_FieldKey:
return exprStructField(factory, e.GetId(), e)
case *exprpb.Expr_CreateStruct_Entry_MapKey:
return exprMapEntry(factory, e.GetId(), e)
}
return nil, fmt.Errorf("unsupported expr entry kind: %v", e)
}
func exprInternal(factory ExprFactory, e *exprpb.Expr) (Expr, error) {
id := e.GetId()
switch e.GetExprKind().(type) {
case *exprpb.Expr_CallExpr:
return exprCall(factory, id, e.GetCallExpr())
case *exprpb.Expr_ComprehensionExpr:
return exprComprehension(factory, id, e.GetComprehensionExpr())
case *exprpb.Expr_ConstExpr:
return exprLiteral(factory, id, e.GetConstExpr())
case *exprpb.Expr_IdentExpr:
return exprIdent(factory, id, e.GetIdentExpr())
case *exprpb.Expr_ListExpr:
return exprList(factory, id, e.GetListExpr())
case *exprpb.Expr_SelectExpr:
return exprSelect(factory, id, e.GetSelectExpr())
case *exprpb.Expr_StructExpr:
s := e.GetStructExpr()
if s.GetMessageName() != "" {
return exprStruct(factory, id, s)
}
return exprMap(factory, id, s)
}
return factory.NewUnspecifiedExpr(id), nil
}
func exprCall(factory ExprFactory, id int64, call *exprpb.Expr_Call) (Expr, error) {
var err error
args := make([]Expr, len(call.GetArgs()))
for i, a := range call.GetArgs() {
args[i], err = exprInternal(factory, a)
if err != nil {
return nil, err
}
}
if call.GetTarget() == nil {
return factory.NewCall(id, call.GetFunction(), args...), nil
}
target, err := exprInternal(factory, call.GetTarget())
if err != nil {
return nil, err
}
return factory.NewMemberCall(id, call.GetFunction(), target, args...), nil
}
func exprComprehension(factory ExprFactory, id int64, comp *exprpb.Expr_Comprehension) (Expr, error) {
iterRange, err := exprInternal(factory, comp.GetIterRange())
if err != nil {
return nil, err
}
accuInit, err := exprInternal(factory, comp.GetAccuInit())
if err != nil {
return nil, err
}
loopCond, err := exprInternal(factory, comp.GetLoopCondition())
if err != nil {
return nil, err
}
loopStep, err := exprInternal(factory, comp.GetLoopStep())
if err != nil {
return nil, err
}
result, err := exprInternal(factory, comp.GetResult())
if err != nil {
return nil, err
}
return factory.NewComprehensionTwoVar(id,
iterRange,
comp.GetIterVar(),
comp.GetIterVar2(),
comp.GetAccuVar(),
accuInit,
loopCond,
loopStep,
result), nil
}
func exprLiteral(factory ExprFactory, id int64, c *exprpb.Constant) (Expr, error) {
val, err := ConstantToVal(c)
if err != nil {
return nil, err
}
return factory.NewLiteral(id, val), nil
}
func exprIdent(factory ExprFactory, id int64, i *exprpb.Expr_Ident) (Expr, error) {
return factory.NewIdent(id, i.GetName()), nil
}
func exprList(factory ExprFactory, id int64, l *exprpb.Expr_CreateList) (Expr, error) {
elems := make([]Expr, len(l.GetElements()))
for i, e := range l.GetElements() {
elem, err := exprInternal(factory, e)
if err != nil {
return nil, err
}
elems[i] = elem
}
return factory.NewList(id, elems, l.GetOptionalIndices()), nil
}
func exprMap(factory ExprFactory, id int64, s *exprpb.Expr_CreateStruct) (Expr, error) {
entries := make([]EntryExpr, len(s.GetEntries()))
var err error
for i, entry := range s.GetEntries() {
entries[i], err = exprMapEntry(factory, entry.GetId(), entry)
if err != nil {
return nil, err
}
}
return factory.NewMap(id, entries), nil
}
func exprMapEntry(factory ExprFactory, id int64, e *exprpb.Expr_CreateStruct_Entry) (EntryExpr, error) {
k, err := exprInternal(factory, e.GetMapKey())
if err != nil {
return nil, err
}
v, err := exprInternal(factory, e.GetValue())
if err != nil {
return nil, err
}
return factory.NewMapEntry(id, k, v, e.GetOptionalEntry()), nil
}
func exprSelect(factory ExprFactory, id int64, s *exprpb.Expr_Select) (Expr, error) {
op, err := exprInternal(factory, s.GetOperand())
if err != nil {
return nil, err
}
if s.GetTestOnly() {
return factory.NewPresenceTest(id, op, s.GetField()), nil
}
return factory.NewSelect(id, op, s.GetField()), nil
}
func exprStruct(factory ExprFactory, id int64, s *exprpb.Expr_CreateStruct) (Expr, error) {
fields := make([]EntryExpr, len(s.GetEntries()))
var err error
for i, field := range s.GetEntries() {
fields[i], err = exprStructField(factory, field.GetId(), field)
if err != nil {
return nil, err
}
}
return factory.NewStruct(id, s.GetMessageName(), fields), nil
}
func exprStructField(factory ExprFactory, id int64, f *exprpb.Expr_CreateStruct_Entry) (EntryExpr, error) {
v, err := exprInternal(factory, f.GetValue())
if err != nil {
return nil, err
}
return factory.NewStructField(id, f.GetFieldKey(), v, f.GetOptionalEntry()), nil
}
// ExprToProto serializes an ast.Expr value to a protobuf Expr representation.
func ExprToProto(e Expr) (*exprpb.Expr, error) {
if e == nil {
return &exprpb.Expr{}, nil
}
switch e.Kind() {
case CallKind:
return protoCall(e.ID(), e.AsCall())
case ComprehensionKind:
return protoComprehension(e.ID(), e.AsComprehension())
case IdentKind:
return protoIdent(e.ID(), e.AsIdent())
case ListKind:
return protoList(e.ID(), e.AsList())
case LiteralKind:
return protoLiteral(e.ID(), e.AsLiteral())
case MapKind:
return protoMap(e.ID(), e.AsMap())
case SelectKind:
return protoSelect(e.ID(), e.AsSelect())
case StructKind:
return protoStruct(e.ID(), e.AsStruct())
case UnspecifiedExprKind:
// Handle the case where a macro reference may be getting translated.
// A nested macro 'pointer' is a non-zero expression id with no kind set.
if e.ID() != 0 {
return &exprpb.Expr{Id: e.ID()}, nil
}
return &exprpb.Expr{}, nil
}
return nil, fmt.Errorf("unsupported expr kind: %v", e)
}
// EntryExprToProto converts an ast.EntryExpr to a protobuf CreateStruct entry
func EntryExprToProto(e EntryExpr) (*exprpb.Expr_CreateStruct_Entry, error) {
switch e.Kind() {
case MapEntryKind:
return protoMapEntry(e.ID(), e.AsMapEntry())
case StructFieldKind:
return protoStructField(e.ID(), e.AsStructField())
case UnspecifiedEntryExprKind:
return &exprpb.Expr_CreateStruct_Entry{}, nil
}
return nil, fmt.Errorf("unsupported expr entry kind: %v", e)
}
func protoCall(id int64, call CallExpr) (*exprpb.Expr, error) {
var err error
var target *exprpb.Expr
if call.IsMemberFunction() {
target, err = ExprToProto(call.Target())
if err != nil {
return nil, err
}
}
callArgs := call.Args()
args := make([]*exprpb.Expr, len(callArgs))
for i, a := range callArgs {
args[i], err = ExprToProto(a)
if err != nil {
return nil, err
}
}
return &exprpb.Expr{
Id: id,
ExprKind: &exprpb.Expr_CallExpr{
CallExpr: &exprpb.Expr_Call{
Function: call.FunctionName(),
Target: target,
Args: args,
},
},
}, nil
}
func protoComprehension(id int64, comp ComprehensionExpr) (*exprpb.Expr, error) {
iterRange, err := ExprToProto(comp.IterRange())
if err != nil {
return nil, err
}
accuInit, err := ExprToProto(comp.AccuInit())
if err != nil {
return nil, err
}
loopCond, err := ExprToProto(comp.LoopCondition())
if err != nil {
return nil, err
}
loopStep, err := ExprToProto(comp.LoopStep())
if err != nil {
return nil, err
}
result, err := ExprToProto(comp.Result())
if err != nil {
return nil, err
}
return &exprpb.Expr{
Id: id,
ExprKind: &exprpb.Expr_ComprehensionExpr{
ComprehensionExpr: &exprpb.Expr_Comprehension{
IterVar: comp.IterVar(),
IterVar2: comp.IterVar2(),
IterRange: iterRange,
AccuVar: comp.AccuVar(),
AccuInit: accuInit,
LoopCondition: loopCond,
LoopStep: loopStep,
Result: result,
},
},
}, nil
}
func protoIdent(id int64, name string) (*exprpb.Expr, error) {
return &exprpb.Expr{
Id: id,
ExprKind: &exprpb.Expr_IdentExpr{
IdentExpr: &exprpb.Expr_Ident{
Name: name,
},
},
}, nil
}
func protoList(id int64, list ListExpr) (*exprpb.Expr, error) {
var err error
elems := make([]*exprpb.Expr, list.Size())
for i, e := range list.Elements() {
elems[i], err = ExprToProto(e)
if err != nil {
return nil, err
}
}
return &exprpb.Expr{
Id: id,
ExprKind: &exprpb.Expr_ListExpr{
ListExpr: &exprpb.Expr_CreateList{
Elements: elems,
OptionalIndices: list.OptionalIndices(),
},
},
}, nil
}
func protoLiteral(id int64, val ref.Val) (*exprpb.Expr, error) {
c, err := ValToConstant(val)
if err != nil {
return nil, err
}
return &exprpb.Expr{
Id: id,
ExprKind: &exprpb.Expr_ConstExpr{
ConstExpr: c,
},
}, nil
}
func protoMap(id int64, m MapExpr) (*exprpb.Expr, error) {
entries := make([]*exprpb.Expr_CreateStruct_Entry, len(m.Entries()))
var err error
for i, e := range m.Entries() {
entries[i], err = EntryExprToProto(e)
if err != nil {
return nil, err
}
}
return &exprpb.Expr{
Id: id,
ExprKind: &exprpb.Expr_StructExpr{
StructExpr: &exprpb.Expr_CreateStruct{
Entries: entries,
},
},
}, nil
}
func protoMapEntry(id int64, e MapEntry) (*exprpb.Expr_CreateStruct_Entry, error) {
k, err := ExprToProto(e.Key())
if err != nil {
return nil, err
}
v, err := ExprToProto(e.Value())
if err != nil {
return nil, err
}
return &exprpb.Expr_CreateStruct_Entry{
Id: id,
KeyKind: &exprpb.Expr_CreateStruct_Entry_MapKey{
MapKey: k,
},
Value: v,
OptionalEntry: e.IsOptional(),
}, nil
}
func protoSelect(id int64, s SelectExpr) (*exprpb.Expr, error) {
op, err := ExprToProto(s.Operand())
if err != nil {
return nil, err
}
return &exprpb.Expr{
Id: id,
ExprKind: &exprpb.Expr_SelectExpr{
SelectExpr: &exprpb.Expr_Select{
Operand: op,
Field: s.FieldName(),
TestOnly: s.IsTestOnly(),
},
},
}, nil
}
func protoStruct(id int64, s StructExpr) (*exprpb.Expr, error) {
entries := make([]*exprpb.Expr_CreateStruct_Entry, len(s.Fields()))
var err error
for i, e := range s.Fields() {
entries[i], err = EntryExprToProto(e)
if err != nil {
return nil, err
}
}
return &exprpb.Expr{
Id: id,
ExprKind: &exprpb.Expr_StructExpr{
StructExpr: &exprpb.Expr_CreateStruct{
MessageName: s.TypeName(),
Entries: entries,
},
},
}, nil
}
func protoStructField(id int64, f StructField) (*exprpb.Expr_CreateStruct_Entry, error) {
v, err := ExprToProto(f.Value())
if err != nil {
return nil, err
}
return &exprpb.Expr_CreateStruct_Entry{
Id: id,
KeyKind: &exprpb.Expr_CreateStruct_Entry_FieldKey{
FieldKey: f.Name(),
},
Value: v,
OptionalEntry: f.IsOptional(),
}, nil
}
// SourceInfoToProto serializes an ast.SourceInfo value to a protobuf SourceInfo object.
func SourceInfoToProto(info *SourceInfo) (*exprpb.SourceInfo, error) {
if info == nil {
return &exprpb.SourceInfo{}, nil
}
sourceInfo := &exprpb.SourceInfo{
SyntaxVersion: info.SyntaxVersion(),
Location: info.Description(),
LineOffsets: info.LineOffsets(),
Positions: make(map[int64]int32, len(info.OffsetRanges())),
MacroCalls: make(map[int64]*exprpb.Expr, len(info.MacroCalls())),
}
for id, offset := range info.OffsetRanges() {
sourceInfo.Positions[id] = offset.Start
}
for id, e := range info.MacroCalls() {
call, err := ExprToProto(e)
if err != nil {
return nil, err
}
sourceInfo.MacroCalls[id] = call
}
return sourceInfo, nil
}
// ProtoToSourceInfo deserializes the protobuf into a native SourceInfo value.
func ProtoToSourceInfo(info *exprpb.SourceInfo) (*SourceInfo, error) {
sourceInfo := &SourceInfo{
syntax: info.GetSyntaxVersion(),
desc: info.GetLocation(),
lines: info.GetLineOffsets(),
offsetRanges: make(map[int64]OffsetRange, len(info.GetPositions())),
macroCalls: make(map[int64]Expr, len(info.GetMacroCalls())),
}
for id, offset := range info.GetPositions() {
sourceInfo.SetOffsetRange(id, OffsetRange{Start: offset, Stop: offset})
}
for id, e := range info.GetMacroCalls() {
call, err := ProtoToExpr(e)
if err != nil {
return nil, err
}
sourceInfo.SetMacroCall(id, call)
}
return sourceInfo, nil
}
// ReferenceInfoToProto converts a ReferenceInfo instance to a protobuf Reference suitable for serialization.
func ReferenceInfoToProto(info *ReferenceInfo) (*exprpb.Reference, error) {
c, err := ValToConstant(info.Value)
if err != nil {
return nil, err
}
return &exprpb.Reference{
Name: info.Name,
OverloadId: info.OverloadIDs,
Value: c,
}, nil
}
// ProtoToReferenceInfo converts a protobuf Reference into a CEL-native ReferenceInfo instance.
func ProtoToReferenceInfo(ref *exprpb.Reference) (*ReferenceInfo, error) {
v, err := ConstantToVal(ref.GetValue())
if err != nil {
return nil, err
}
return &ReferenceInfo{
Name: ref.GetName(),
OverloadIDs: ref.GetOverloadId(),
Value: v,
}, nil
}
// ValToConstant converts a CEL-native ref.Val to a protobuf Constant.
//
// Only simple scalar types are supported by this method.
func ValToConstant(v ref.Val) (*exprpb.Constant, error) {
if v == nil {
return nil, nil
}
switch v.Type() {
case types.BoolType:
return &exprpb.Constant{ConstantKind: &exprpb.Constant_BoolValue{BoolValue: v.Value().(bool)}}, nil
case types.BytesType:
return &exprpb.Constant{ConstantKind: &exprpb.Constant_BytesValue{BytesValue: v.Value().([]byte)}}, nil
case types.DoubleType:
return &exprpb.Constant{ConstantKind: &exprpb.Constant_DoubleValue{DoubleValue: v.Value().(float64)}}, nil
case types.IntType:
return &exprpb.Constant{ConstantKind: &exprpb.Constant_Int64Value{Int64Value: v.Value().(int64)}}, nil
case types.NullType:
return &exprpb.Constant{ConstantKind: &exprpb.Constant_NullValue{NullValue: structpb.NullValue_NULL_VALUE}}, nil
case types.StringType:
return &exprpb.Constant{ConstantKind: &exprpb.Constant_StringValue{StringValue: v.Value().(string)}}, nil
case types.UintType:
return &exprpb.Constant{ConstantKind: &exprpb.Constant_Uint64Value{Uint64Value: v.Value().(uint64)}}, nil
}
return nil, fmt.Errorf("unsupported constant kind: %v", v.Type())
}
// ConstantToVal converts a protobuf Constant to a CEL-native ref.Val.
func ConstantToVal(c *exprpb.Constant) (ref.Val, error) {
return AlphaProtoConstantAsVal(c)
}
// AlphaProtoConstantAsVal converts a v1alpha1.Constant protobuf to a CEL-native ref.Val.
func AlphaProtoConstantAsVal(c *exprpb.Constant) (ref.Val, error) {
if c == nil {
return nil, nil
}
canonical := &celpb.Constant{}
if err := convertProto(c, canonical); err != nil {
return nil, err
}
return ProtoConstantAsVal(canonical)
}
// ProtoConstantAsVal converts a canonical celpb.Constant protobuf to a CEL-native ref.Val.
func ProtoConstantAsVal(c *celpb.Constant) (ref.Val, error) {
switch c.GetConstantKind().(type) {
case *celpb.Constant_BoolValue:
return types.Bool(c.GetBoolValue()), nil
case *celpb.Constant_BytesValue:
return types.Bytes(c.GetBytesValue()), nil
case *celpb.Constant_DoubleValue:
return types.Double(c.GetDoubleValue()), nil
case *celpb.Constant_Int64Value:
return types.Int(c.GetInt64Value()), nil
case *celpb.Constant_NullValue:
return types.NullValue, nil
case *celpb.Constant_StringValue:
return types.String(c.GetStringValue()), nil
case *celpb.Constant_Uint64Value:
return types.Uint(c.GetUint64Value()), nil
}
return nil, fmt.Errorf("unsupported constant kind: %v", c.GetConstantKind())
}
func convertProto(src, dst proto.Message) error {
pb, err := proto.Marshal(src)
if err != nil {
return err
}
err = proto.Unmarshal(pb, dst)
return err
}
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package ast
import (
"github.com/google/cel-go/common/types/ref"
)
// ExprKind represents the expression node kind.
type ExprKind int
const (
// UnspecifiedExprKind represents an unset expression with no specified properties.
UnspecifiedExprKind ExprKind = iota
// CallKind represents a function call.
CallKind
// ComprehensionKind represents a comprehension expression generated by a macro.
ComprehensionKind
// IdentKind represents a simple variable, constant, or type identifier.
IdentKind
// ListKind represents a list literal expression.
ListKind
// LiteralKind represents a primitive scalar literal.
LiteralKind
// MapKind represents a map literal expression.
MapKind
// SelectKind represents a field selection expression.
SelectKind
// StructKind represents a struct literal expression.
StructKind
)
// Expr represents the base expression node in a CEL abstract syntax tree.
//
// Depending on the `Kind()` value, the Expr may be converted to a concrete expression types
// as indicated by the `As<Kind>` methods.
type Expr interface {
// ID of the expression as it appears in the AST
ID() int64
// Kind of the expression node. See ExprKind for the valid enum values.
Kind() ExprKind
// AsCall adapts the expr into a CallExpr
//
// The Kind() must be equal to a CallKind for the conversion to be well-defined.
AsCall() CallExpr
// AsComprehension adapts the expr into a ComprehensionExpr.
//
// The Kind() must be equal to a ComprehensionKind for the conversion to be well-defined.
AsComprehension() ComprehensionExpr
// AsIdent adapts the expr into an identifier string.
//
// The Kind() must be equal to an IdentKind for the conversion to be well-defined.
AsIdent() string
// AsLiteral adapts the expr into a constant ref.Val.
//
// The Kind() must be equal to a LiteralKind for the conversion to be well-defined.
AsLiteral() ref.Val
// AsList adapts the expr into a ListExpr.
//
// The Kind() must be equal to a ListKind for the conversion to be well-defined.
AsList() ListExpr
// AsMap adapts the expr into a MapExpr.
//
// The Kind() must be equal to a MapKind for the conversion to be well-defined.
AsMap() MapExpr
// AsSelect adapts the expr into a SelectExpr.
//
// The Kind() must be equal to a SelectKind for the conversion to be well-defined.
AsSelect() SelectExpr
// AsStruct adapts the expr into a StructExpr.
//
// The Kind() must be equal to a StructKind for the conversion to be well-defined.
AsStruct() StructExpr
// RenumberIDs performs an in-place update of the expression and all of its descendents numeric ids.
RenumberIDs(IDGenerator)
// SetKindCase replaces the contents of the current expression with the contents of the other.
//
// The SetKindCase takes ownership of any expression instances references within the input Expr.
// A shallow copy is made of the Expr value itself, but not a deep one.
//
// This method should only be used during AST rewrites using temporary Expr values.
SetKindCase(Expr)
// isExpr is a marker interface.
isExpr()
}
// EntryExprKind represents the possible EntryExpr kinds.
type EntryExprKind int
const (
// UnspecifiedEntryExprKind indicates that the entry expr is not set.
UnspecifiedEntryExprKind EntryExprKind = iota
// MapEntryKind indicates that the entry is a MapEntry type with key and value expressions.
MapEntryKind
// StructFieldKind indicates that the entry is a StructField with a field name and initializer
// expression.
StructFieldKind
)
// EntryExpr represents the base entry expression in a CEL map or struct literal.
type EntryExpr interface {
// ID of the entry as it appears in the AST.
ID() int64
// Kind of the entry expression node. See EntryExprKind for valid enum values.
Kind() EntryExprKind
// AsMapEntry casts the EntryExpr to a MapEntry.
//
// The Kind() must be equal to MapEntryKind for the conversion to be well-defined.
AsMapEntry() MapEntry
// AsStructField casts the EntryExpr to a StructField
//
// The Kind() must be equal to StructFieldKind for the conversion to be well-defined.
AsStructField() StructField
// RenumberIDs performs an in-place update of the expression and all of its descendents numeric ids.
RenumberIDs(IDGenerator)
isEntryExpr()
}
// IDGenerator produces unique ids suitable for tagging expression nodes
type IDGenerator func(originalID int64) int64
// CallExpr defines an interface for inspecting a function call and its arguments.
type CallExpr interface {
// FunctionName returns the name of the function.
FunctionName() string
// IsMemberFunction returns whether the call has a non-nil target indicating it is a member function
IsMemberFunction() bool
// Target returns the target of the expression if one is present.
Target() Expr
// Args returns the list of call arguments, excluding the target.
Args() []Expr
// marker interface method
isExpr()
}
// ListExpr defines an interface for inspecting a list literal expression.
type ListExpr interface {
// Elements returns the list elements as navigable expressions.
Elements() []Expr
// OptionalIndicies returns the list of optional indices in the list literal.
OptionalIndices() []int32
// IsOptional indicates whether the given element index is optional.
IsOptional(int32) bool
// Size returns the number of elements in the list.
Size() int
// marker interface method
isExpr()
}
// SelectExpr defines an interface for inspecting a select expression.
type SelectExpr interface {
// Operand returns the selection operand expression.
Operand() Expr
// FieldName returns the field name being selected from the operand.
FieldName() string
// IsTestOnly indicates whether the select expression is a presence test generated by a macro.
IsTestOnly() bool
// marker interface method
isExpr()
}
// MapExpr defines an interface for inspecting a map expression.
type MapExpr interface {
// Entries returns the map key value pairs as EntryExpr values.
Entries() []EntryExpr
// Size returns the number of entries in the map.
Size() int
// marker interface method
isExpr()
}
// MapEntry defines an interface for inspecting a map entry.
type MapEntry interface {
// Key returns the map entry key expression.
Key() Expr
// Value returns the map entry value expression.
Value() Expr
// IsOptional returns whether the entry is optional.
IsOptional() bool
// marker interface method
isEntryExpr()
}
// StructExpr defines an interfaces for inspecting a struct and its field initializers.
type StructExpr interface {
// TypeName returns the struct type name.
TypeName() string
// Fields returns the set of field initializers in the struct expression as EntryExpr values.
Fields() []EntryExpr
// marker interface method
isExpr()
}
// StructField defines an interface for inspecting a struct field initialization.
type StructField interface {
// Name returns the name of the field.
Name() string
// Value returns the field initialization expression.
Value() Expr
// IsOptional returns whether the field is optional.
IsOptional() bool
// marker interface method
isEntryExpr()
}
// ComprehensionExpr defines an interface for inspecting a comprehension expression.
type ComprehensionExpr interface {
// IterRange returns the iteration range expression.
IterRange() Expr
// IterVar returns the iteration variable name.
//
// For one-variable comprehensions, the iter var refers to the element value
// when iterating over a list, or the map key when iterating over a map.
//
// For two-variable comprehneions, the iter var refers to the list index or the
// map key.
IterVar() string
// IterVar2 returns the second iteration variable name.
//
// When the value is non-empty, the comprehension is a two-variable comprehension.
IterVar2() string
// HasIterVar2 returns true if the second iteration variable is non-empty.
HasIterVar2() bool
// AccuVar returns the accumulation variable name.
AccuVar() string
// AccuInit returns the accumulation variable initialization expression.
AccuInit() Expr
// LoopCondition returns the loop condition expression.
LoopCondition() Expr
// LoopStep returns the loop step expression.
LoopStep() Expr
// Result returns the comprehension result expression.
Result() Expr
// marker interface method
isExpr()
}
var _ Expr = &expr{}
type expr struct {
id int64
exprKindCase
}
type exprKindCase interface {
Kind() ExprKind
renumberIDs(IDGenerator)
isExpr()
}
func (e *expr) ID() int64 {
if e == nil {
return 0
}
return e.id
}
func (e *expr) Kind() ExprKind {
if e == nil || e.exprKindCase == nil {
return UnspecifiedExprKind
}
return e.exprKindCase.Kind()
}
func (e *expr) AsCall() CallExpr {
if e.Kind() != CallKind {
return nilCall
}
return e.exprKindCase.(CallExpr)
}
func (e *expr) AsComprehension() ComprehensionExpr {
if e.Kind() != ComprehensionKind {
return nilCompre
}
return e.exprKindCase.(ComprehensionExpr)
}
func (e *expr) AsIdent() string {
if e.Kind() != IdentKind {
return ""
}
return string(e.exprKindCase.(baseIdentExpr))
}
func (e *expr) AsLiteral() ref.Val {
if e.Kind() != LiteralKind {
return nil
}
return e.exprKindCase.(*baseLiteral).Val
}
func (e *expr) AsList() ListExpr {
if e.Kind() != ListKind {
return nilList
}
return e.exprKindCase.(ListExpr)
}
func (e *expr) AsMap() MapExpr {
if e.Kind() != MapKind {
return nilMap
}
return e.exprKindCase.(MapExpr)
}
func (e *expr) AsSelect() SelectExpr {
if e.Kind() != SelectKind {
return nilSel
}
return e.exprKindCase.(SelectExpr)
}
func (e *expr) AsStruct() StructExpr {
if e.Kind() != StructKind {
return nilStruct
}
return e.exprKindCase.(StructExpr)
}
func (e *expr) SetKindCase(other Expr) {
if e == nil {
return
}
if other == nil {
e.exprKindCase = nil
return
}
switch other.Kind() {
case CallKind:
c := other.AsCall()
e.exprKindCase = &baseCallExpr{
function: c.FunctionName(),
target: c.Target(),
args: c.Args(),
isMember: c.IsMemberFunction(),
}
case ComprehensionKind:
c := other.AsComprehension()
e.exprKindCase = &baseComprehensionExpr{
iterRange: c.IterRange(),
iterVar: c.IterVar(),
iterVar2: c.IterVar2(),
accuVar: c.AccuVar(),
accuInit: c.AccuInit(),
loopCond: c.LoopCondition(),
loopStep: c.LoopStep(),
result: c.Result(),
}
case IdentKind:
e.exprKindCase = baseIdentExpr(other.AsIdent())
case ListKind:
l := other.AsList()
optIndexMap := make(map[int32]struct{}, len(l.OptionalIndices()))
for _, idx := range l.OptionalIndices() {
optIndexMap[idx] = struct{}{}
}
e.exprKindCase = &baseListExpr{
elements: l.Elements(),
optIndices: l.OptionalIndices(),
optIndexMap: optIndexMap,
}
case LiteralKind:
e.exprKindCase = &baseLiteral{Val: other.AsLiteral()}
case MapKind:
e.exprKindCase = &baseMapExpr{
entries: other.AsMap().Entries(),
}
case SelectKind:
s := other.AsSelect()
e.exprKindCase = &baseSelectExpr{
operand: s.Operand(),
field: s.FieldName(),
testOnly: s.IsTestOnly(),
}
case StructKind:
s := other.AsStruct()
e.exprKindCase = &baseStructExpr{
typeName: s.TypeName(),
fields: s.Fields(),
}
case UnspecifiedExprKind:
e.exprKindCase = nil
}
}
func (e *expr) RenumberIDs(idGen IDGenerator) {
if e == nil {
return
}
e.id = idGen(e.id)
if e.exprKindCase != nil {
e.exprKindCase.renumberIDs(idGen)
}
}
type baseCallExpr struct {
function string
target Expr
args []Expr
isMember bool
}
func (*baseCallExpr) Kind() ExprKind {
return CallKind
}
func (e *baseCallExpr) FunctionName() string {
if e == nil {
return ""
}
return e.function
}
func (e *baseCallExpr) IsMemberFunction() bool {
if e == nil {
return false
}
return e.isMember
}
func (e *baseCallExpr) Target() Expr {
if e == nil || !e.IsMemberFunction() {
return nilExpr
}
return e.target
}
func (e *baseCallExpr) Args() []Expr {
if e == nil {
return []Expr{}
}
return e.args
}
func (e *baseCallExpr) renumberIDs(idGen IDGenerator) {
if e.IsMemberFunction() {
e.Target().RenumberIDs(idGen)
}
for _, arg := range e.Args() {
arg.RenumberIDs(idGen)
}
}
func (*baseCallExpr) isExpr() {}
var _ ComprehensionExpr = &baseComprehensionExpr{}
type baseComprehensionExpr struct {
iterRange Expr
iterVar string
iterVar2 string
accuVar string
accuInit Expr
loopCond Expr
loopStep Expr
result Expr
}
func (*baseComprehensionExpr) Kind() ExprKind {
return ComprehensionKind
}
func (e *baseComprehensionExpr) IterRange() Expr {
if e == nil {
return nilExpr
}
return e.iterRange
}
func (e *baseComprehensionExpr) IterVar() string {
return e.iterVar
}
func (e *baseComprehensionExpr) IterVar2() string {
return e.iterVar2
}
func (e *baseComprehensionExpr) HasIterVar2() bool {
return e.iterVar2 != ""
}
func (e *baseComprehensionExpr) AccuVar() string {
return e.accuVar
}
func (e *baseComprehensionExpr) AccuInit() Expr {
if e == nil {
return nilExpr
}
return e.accuInit
}
func (e *baseComprehensionExpr) LoopCondition() Expr {
if e == nil {
return nilExpr
}
return e.loopCond
}
func (e *baseComprehensionExpr) LoopStep() Expr {
if e == nil {
return nilExpr
}
return e.loopStep
}
func (e *baseComprehensionExpr) Result() Expr {
if e == nil {
return nilExpr
}
return e.result
}
func (e *baseComprehensionExpr) renumberIDs(idGen IDGenerator) {
e.IterRange().RenumberIDs(idGen)
e.AccuInit().RenumberIDs(idGen)
e.LoopCondition().RenumberIDs(idGen)
e.LoopStep().RenumberIDs(idGen)
e.Result().RenumberIDs(idGen)
}
func (*baseComprehensionExpr) isExpr() {}
var _ exprKindCase = baseIdentExpr("")
type baseIdentExpr string
func (baseIdentExpr) Kind() ExprKind {
return IdentKind
}
func (e baseIdentExpr) renumberIDs(IDGenerator) {}
func (baseIdentExpr) isExpr() {}
var _ exprKindCase = &baseLiteral{}
var _ ref.Val = &baseLiteral{}
type baseLiteral struct {
ref.Val
}
func (*baseLiteral) Kind() ExprKind {
return LiteralKind
}
func (l *baseLiteral) renumberIDs(IDGenerator) {}
func (*baseLiteral) isExpr() {}
var _ ListExpr = &baseListExpr{}
type baseListExpr struct {
elements []Expr
optIndices []int32
optIndexMap map[int32]struct{}
}
func (*baseListExpr) Kind() ExprKind {
return ListKind
}
func (e *baseListExpr) Elements() []Expr {
if e == nil {
return []Expr{}
}
return e.elements
}
func (e *baseListExpr) IsOptional(index int32) bool {
_, found := e.optIndexMap[index]
return found
}
func (e *baseListExpr) OptionalIndices() []int32 {
if e == nil {
return []int32{}
}
return e.optIndices
}
func (e *baseListExpr) Size() int {
return len(e.Elements())
}
func (e *baseListExpr) renumberIDs(idGen IDGenerator) {
for _, elem := range e.Elements() {
elem.RenumberIDs(idGen)
}
}
func (*baseListExpr) isExpr() {}
type baseMapExpr struct {
entries []EntryExpr
}
func (*baseMapExpr) Kind() ExprKind {
return MapKind
}
func (e *baseMapExpr) Entries() []EntryExpr {
if e == nil {
return []EntryExpr{}
}
return e.entries
}
func (e *baseMapExpr) Size() int {
return len(e.Entries())
}
func (e *baseMapExpr) renumberIDs(idGen IDGenerator) {
for _, entry := range e.Entries() {
entry.RenumberIDs(idGen)
}
}
func (*baseMapExpr) isExpr() {}
type baseSelectExpr struct {
operand Expr
field string
testOnly bool
}
func (*baseSelectExpr) Kind() ExprKind {
return SelectKind
}
func (e *baseSelectExpr) Operand() Expr {
if e == nil || e.operand == nil {
return nilExpr
}
return e.operand
}
func (e *baseSelectExpr) FieldName() string {
if e == nil {
return ""
}
return e.field
}
func (e *baseSelectExpr) IsTestOnly() bool {
if e == nil {
return false
}
return e.testOnly
}
func (e *baseSelectExpr) renumberIDs(idGen IDGenerator) {
e.Operand().RenumberIDs(idGen)
}
func (*baseSelectExpr) isExpr() {}
type baseStructExpr struct {
typeName string
fields []EntryExpr
}
func (*baseStructExpr) Kind() ExprKind {
return StructKind
}
func (e *baseStructExpr) TypeName() string {
if e == nil {
return ""
}
return e.typeName
}
func (e *baseStructExpr) Fields() []EntryExpr {
if e == nil {
return []EntryExpr{}
}
return e.fields
}
func (e *baseStructExpr) renumberIDs(idGen IDGenerator) {
for _, f := range e.Fields() {
f.RenumberIDs(idGen)
}
}
func (*baseStructExpr) isExpr() {}
type entryExprKindCase interface {
Kind() EntryExprKind
renumberIDs(IDGenerator)
isEntryExpr()
}
var _ EntryExpr = &entryExpr{}
type entryExpr struct {
id int64
entryExprKindCase
}
func (e *entryExpr) ID() int64 {
return e.id
}
func (e *entryExpr) AsMapEntry() MapEntry {
if e.Kind() != MapEntryKind {
return nilMapEntry
}
return e.entryExprKindCase.(MapEntry)
}
func (e *entryExpr) AsStructField() StructField {
if e.Kind() != StructFieldKind {
return nilStructField
}
return e.entryExprKindCase.(StructField)
}
func (e *entryExpr) RenumberIDs(idGen IDGenerator) {
e.id = idGen(e.id)
e.entryExprKindCase.renumberIDs(idGen)
}
type baseMapEntry struct {
key Expr
value Expr
isOptional bool
}
func (e *baseMapEntry) Kind() EntryExprKind {
return MapEntryKind
}
func (e *baseMapEntry) Key() Expr {
if e == nil {
return nilExpr
}
return e.key
}
func (e *baseMapEntry) Value() Expr {
if e == nil {
return nilExpr
}
return e.value
}
func (e *baseMapEntry) IsOptional() bool {
if e == nil {
return false
}
return e.isOptional
}
func (e *baseMapEntry) renumberIDs(idGen IDGenerator) {
e.Key().RenumberIDs(idGen)
e.Value().RenumberIDs(idGen)
}
func (*baseMapEntry) isEntryExpr() {}
type baseStructField struct {
field string
value Expr
isOptional bool
}
func (f *baseStructField) Kind() EntryExprKind {
return StructFieldKind
}
func (f *baseStructField) Name() string {
if f == nil {
return ""
}
return f.field
}
func (f *baseStructField) Value() Expr {
if f == nil {
return nilExpr
}
return f.value
}
func (f *baseStructField) IsOptional() bool {
if f == nil {
return false
}
return f.isOptional
}
func (f *baseStructField) renumberIDs(idGen IDGenerator) {
f.Value().RenumberIDs(idGen)
}
func (*baseStructField) isEntryExpr() {}
var (
nilExpr *expr = nil
nilCall *baseCallExpr = nil
nilCompre *baseComprehensionExpr = nil
nilList *baseListExpr = nil
nilMap *baseMapExpr = nil
nilMapEntry *baseMapEntry = nil
nilSel *baseSelectExpr = nil
nilStruct *baseStructExpr = nil
nilStructField *baseStructField = nil
)
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package ast
import "github.com/google/cel-go/common/types/ref"
// ExprFactory interfaces defines a set of methods necessary for building native expression values.
type ExprFactory interface {
// CopyExpr creates a deep copy of the input Expr value.
CopyExpr(Expr) Expr
// CopyEntryExpr creates a deep copy of the input EntryExpr value.
CopyEntryExpr(EntryExpr) EntryExpr
// NewCall creates an Expr value representing a global function call.
NewCall(id int64, function string, args ...Expr) Expr
// NewComprehension creates an Expr value representing a one-variable comprehension over a value range.
NewComprehension(id int64, iterRange Expr, iterVar, accuVar string, accuInit, loopCondition, loopStep, result Expr) Expr
// NewComprehensionTwoVar creates an Expr value representing a two-variable comprehension over a value range.
NewComprehensionTwoVar(id int64, iterRange Expr, iterVar, iterVar2, accuVar string, accuInit, loopCondition, loopStep, result Expr) Expr
// NewMemberCall creates an Expr value representing a member function call.
NewMemberCall(id int64, function string, receiver Expr, args ...Expr) Expr
// NewIdent creates an Expr value representing an identifier.
NewIdent(id int64, name string) Expr
// NewAccuIdent creates an Expr value representing an accumulator identifier within a
// comprehension.
NewAccuIdent(id int64) Expr
// AccuIdentName reports the name of the accumulator variable to be used within a comprehension.
AccuIdentName() string
// NewLiteral creates an Expr value representing a literal value, such as a string or integer.
NewLiteral(id int64, value ref.Val) Expr
// NewList creates an Expr value representing a list literal expression with optional indices.
//
// Optional indices will typically be empty unless the CEL optional types are enabled.
NewList(id int64, elems []Expr, optIndices []int32) Expr
// NewMap creates an Expr value representing a map literal expression
NewMap(id int64, entries []EntryExpr) Expr
// NewMapEntry creates a MapEntry with a given key, value, and a flag indicating whether
// the key is optionally set.
NewMapEntry(id int64, key, value Expr, isOptional bool) EntryExpr
// NewPresenceTest creates an Expr representing a field presence test on an operand expression.
NewPresenceTest(id int64, operand Expr, field string) Expr
// NewSelect creates an Expr representing a field selection on an operand expression.
NewSelect(id int64, operand Expr, field string) Expr
// NewStruct creates an Expr value representing a struct literal with a given type name and a
// set of field initializers.
NewStruct(id int64, typeName string, fields []EntryExpr) Expr
// NewStructField creates a StructField with a given field name, value, and a flag indicating
// whether the field is optionally set.
NewStructField(id int64, field string, value Expr, isOptional bool) EntryExpr
// NewUnspecifiedExpr creates an empty expression node.
NewUnspecifiedExpr(id int64) Expr
isExprFactory()
}
type baseExprFactory struct {
accumulatorName string
}
// NewExprFactory creates an ExprFactory instance.
func NewExprFactory() ExprFactory {
return &baseExprFactory{
"@result",
}
}
// NewExprFactoryWithAccumulator creates an ExprFactory instance with a custom
// accumulator identifier name.
func NewExprFactoryWithAccumulator(id string) ExprFactory {
return &baseExprFactory{
id,
}
}
func (fac *baseExprFactory) NewCall(id int64, function string, args ...Expr) Expr {
if len(args) == 0 {
args = []Expr{}
}
return fac.newExpr(
id,
&baseCallExpr{
function: function,
target: nilExpr,
args: args,
isMember: false,
})
}
func (fac *baseExprFactory) NewMemberCall(id int64, function string, target Expr, args ...Expr) Expr {
if len(args) == 0 {
args = []Expr{}
}
return fac.newExpr(
id,
&baseCallExpr{
function: function,
target: target,
args: args,
isMember: true,
})
}
func (fac *baseExprFactory) NewComprehension(id int64, iterRange Expr, iterVar, accuVar string, accuInit, loopCond, loopStep, result Expr) Expr {
// Set the iter_var2 to empty string to indicate the second variable is omitted
return fac.NewComprehensionTwoVar(id, iterRange, iterVar, "", accuVar, accuInit, loopCond, loopStep, result)
}
func (fac *baseExprFactory) NewComprehensionTwoVar(id int64, iterRange Expr, iterVar, iterVar2, accuVar string, accuInit, loopCond, loopStep, result Expr) Expr {
return fac.newExpr(
id,
&baseComprehensionExpr{
iterRange: iterRange,
iterVar: iterVar,
iterVar2: iterVar2,
accuVar: accuVar,
accuInit: accuInit,
loopCond: loopCond,
loopStep: loopStep,
result: result,
})
}
func (fac *baseExprFactory) NewIdent(id int64, name string) Expr {
return fac.newExpr(id, baseIdentExpr(name))
}
func (fac *baseExprFactory) NewAccuIdent(id int64) Expr {
return fac.NewIdent(id, fac.AccuIdentName())
}
func (fac *baseExprFactory) AccuIdentName() string {
return fac.accumulatorName
}
func (fac *baseExprFactory) NewLiteral(id int64, value ref.Val) Expr {
return fac.newExpr(id, &baseLiteral{Val: value})
}
func (fac *baseExprFactory) NewList(id int64, elems []Expr, optIndices []int32) Expr {
optIndexMap := make(map[int32]struct{}, len(optIndices))
for _, idx := range optIndices {
optIndexMap[idx] = struct{}{}
}
return fac.newExpr(id,
&baseListExpr{
elements: elems,
optIndices: optIndices,
optIndexMap: optIndexMap,
})
}
func (fac *baseExprFactory) NewMap(id int64, entries []EntryExpr) Expr {
return fac.newExpr(id, &baseMapExpr{entries: entries})
}
func (fac *baseExprFactory) NewMapEntry(id int64, key, value Expr, isOptional bool) EntryExpr {
return fac.newEntryExpr(
id,
&baseMapEntry{
key: key,
value: value,
isOptional: isOptional,
})
}
func (fac *baseExprFactory) NewPresenceTest(id int64, operand Expr, field string) Expr {
return fac.newExpr(
id,
&baseSelectExpr{
operand: operand,
field: field,
testOnly: true,
})
}
func (fac *baseExprFactory) NewSelect(id int64, operand Expr, field string) Expr {
return fac.newExpr(
id,
&baseSelectExpr{
operand: operand,
field: field,
})
}
func (fac *baseExprFactory) NewStruct(id int64, typeName string, fields []EntryExpr) Expr {
return fac.newExpr(
id,
&baseStructExpr{
typeName: typeName,
fields: fields,
})
}
func (fac *baseExprFactory) NewStructField(id int64, field string, value Expr, isOptional bool) EntryExpr {
return fac.newEntryExpr(
id,
&baseStructField{
field: field,
value: value,
isOptional: isOptional,
})
}
func (fac *baseExprFactory) NewUnspecifiedExpr(id int64) Expr {
return fac.newExpr(id, nil)
}
func (fac *baseExprFactory) CopyExpr(e Expr) Expr {
// unwrap navigable expressions to avoid unnecessary allocations during copying.
if nav, ok := e.(*navigableExprImpl); ok {
e = nav.Expr
}
switch e.Kind() {
case CallKind:
c := e.AsCall()
argsCopy := make([]Expr, len(c.Args()))
for i, arg := range c.Args() {
argsCopy[i] = fac.CopyExpr(arg)
}
if !c.IsMemberFunction() {
return fac.NewCall(e.ID(), c.FunctionName(), argsCopy...)
}
return fac.NewMemberCall(e.ID(), c.FunctionName(), fac.CopyExpr(c.Target()), argsCopy...)
case ComprehensionKind:
compre := e.AsComprehension()
return fac.NewComprehensionTwoVar(e.ID(),
fac.CopyExpr(compre.IterRange()),
compre.IterVar(),
compre.IterVar2(),
compre.AccuVar(),
fac.CopyExpr(compre.AccuInit()),
fac.CopyExpr(compre.LoopCondition()),
fac.CopyExpr(compre.LoopStep()),
fac.CopyExpr(compre.Result()))
case IdentKind:
return fac.NewIdent(e.ID(), e.AsIdent())
case ListKind:
l := e.AsList()
elemsCopy := make([]Expr, l.Size())
for i, elem := range l.Elements() {
elemsCopy[i] = fac.CopyExpr(elem)
}
return fac.NewList(e.ID(), elemsCopy, l.OptionalIndices())
case LiteralKind:
return fac.NewLiteral(e.ID(), e.AsLiteral())
case MapKind:
m := e.AsMap()
entriesCopy := make([]EntryExpr, m.Size())
for i, entry := range m.Entries() {
entriesCopy[i] = fac.CopyEntryExpr(entry)
}
return fac.NewMap(e.ID(), entriesCopy)
case SelectKind:
s := e.AsSelect()
if s.IsTestOnly() {
return fac.NewPresenceTest(e.ID(), fac.CopyExpr(s.Operand()), s.FieldName())
}
return fac.NewSelect(e.ID(), fac.CopyExpr(s.Operand()), s.FieldName())
case StructKind:
s := e.AsStruct()
fieldsCopy := make([]EntryExpr, len(s.Fields()))
for i, field := range s.Fields() {
fieldsCopy[i] = fac.CopyEntryExpr(field)
}
return fac.NewStruct(e.ID(), s.TypeName(), fieldsCopy)
default:
return fac.NewUnspecifiedExpr(e.ID())
}
}
func (fac *baseExprFactory) CopyEntryExpr(e EntryExpr) EntryExpr {
switch e.Kind() {
case MapEntryKind:
entry := e.AsMapEntry()
return fac.NewMapEntry(e.ID(),
fac.CopyExpr(entry.Key()), fac.CopyExpr(entry.Value()), entry.IsOptional())
case StructFieldKind:
field := e.AsStructField()
return fac.NewStructField(e.ID(),
field.Name(), fac.CopyExpr(field.Value()), field.IsOptional())
default:
return fac.newEntryExpr(e.ID(), nil)
}
}
func (*baseExprFactory) isExprFactory() {}
func (fac *baseExprFactory) newExpr(id int64, e exprKindCase) Expr {
return &expr{
id: id,
exprKindCase: e,
}
}
func (fac *baseExprFactory) newEntryExpr(id int64, e entryExprKindCase) EntryExpr {
return &entryExpr{
id: id,
entryExprKindCase: e,
}
}
var (
defaultFactory = &baseExprFactory{}
)
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package ast
import (
"github.com/google/cel-go/common/types"
"github.com/google/cel-go/common/types/ref"
)
// NavigableExpr represents the base navigable expression value with methods to inspect the
// parent and child expressions.
type NavigableExpr interface {
Expr
// Type of the expression.
//
// If the expression is type-checked, the type check metadata is returned. If the expression
// has not been type-checked, the types.DynType value is returned.
Type() *types.Type
// Parent returns the parent expression node, if one exists.
Parent() (NavigableExpr, bool)
// Children returns a list of child expression nodes.
Children() []NavigableExpr
// Depth indicates the depth in the expression tree.
//
// The root expression has depth 0.
Depth() int
}
// NavigateAST converts an AST to a NavigableExpr
func NavigateAST(ast *AST) NavigableExpr {
return NavigateExpr(ast, ast.Expr())
}
// NavigateExpr creates a NavigableExpr whose type information is backed by the input AST.
//
// If the expression is already a NavigableExpr, the parent and depth information will be
// propagated on the new NavigableExpr value; otherwise, the expr value will be treated
// as though it is the root of the expression graph with a depth of 0.
func NavigateExpr(ast *AST, expr Expr) NavigableExpr {
depth := 0
var parent NavigableExpr = nil
if nav, ok := expr.(NavigableExpr); ok {
depth = nav.Depth()
parent, _ = nav.Parent()
}
return newNavigableExpr(ast, parent, expr, depth)
}
// ExprMatcher takes a NavigableExpr in and indicates whether the value is a match.
//
// This function type should be use with the `Match` and `MatchList` calls.
type ExprMatcher func(NavigableExpr) bool
// ConstantValueMatcher returns an ExprMatcher which will return true if the input NavigableExpr
// is comprised of all constant values, such as a simple literal or even list and map literal.
func ConstantValueMatcher() ExprMatcher {
return matchIsConstantValue
}
// KindMatcher returns an ExprMatcher which will return true if the input NavigableExpr.Kind() matches
// the specified `kind`.
func KindMatcher(kind ExprKind) ExprMatcher {
return func(e NavigableExpr) bool {
return e.Kind() == kind
}
}
// FunctionMatcher returns an ExprMatcher which will match NavigableExpr nodes of CallKind type whose
// function name is equal to `funcName`.
func FunctionMatcher(funcName string) ExprMatcher {
return func(e NavigableExpr) bool {
if e.Kind() != CallKind {
return false
}
return e.AsCall().FunctionName() == funcName
}
}
// AllMatcher returns true for all descendants of a NavigableExpr, effectively flattening them into a list.
//
// Such a result would work well with subsequent MatchList calls.
func AllMatcher() ExprMatcher {
return func(NavigableExpr) bool {
return true
}
}
// MatchDescendants takes a NavigableExpr and ExprMatcher and produces a list of NavigableExpr values
// matching the input criteria in post-order (bottom up).
func MatchDescendants(expr NavigableExpr, matcher ExprMatcher) []NavigableExpr {
matches := []NavigableExpr{}
navVisitor := &baseVisitor{
visitExpr: func(e Expr) {
nav := e.(NavigableExpr)
if matcher(nav) {
matches = append(matches, nav)
}
},
}
visit(expr, navVisitor, postOrder, 0, 0)
return matches
}
// MatchSubset applies an ExprMatcher to a list of NavigableExpr values and their descendants, producing a
// subset of NavigableExpr values which match.
func MatchSubset(exprs []NavigableExpr, matcher ExprMatcher) []NavigableExpr {
matches := []NavigableExpr{}
navVisitor := &baseVisitor{
visitExpr: func(e Expr) {
nav := e.(NavigableExpr)
if matcher(nav) {
matches = append(matches, nav)
}
},
}
for _, expr := range exprs {
visit(expr, navVisitor, postOrder, 0, 1)
}
return matches
}
// Visitor defines an object for visiting Expr and EntryExpr nodes within an expression graph.
type Visitor interface {
// VisitExpr visits the input expression.
VisitExpr(Expr)
// VisitEntryExpr visits the input entry expression, i.e. a struct field or map entry.
VisitEntryExpr(EntryExpr)
}
type baseVisitor struct {
visitExpr func(Expr)
visitEntryExpr func(EntryExpr)
}
// VisitExpr visits the Expr if the internal expr visitor has been configured.
func (v *baseVisitor) VisitExpr(e Expr) {
if v.visitExpr != nil {
v.visitExpr(e)
}
}
// VisitEntryExpr visits the entry if the internal expr entry visitor has been configured.
func (v *baseVisitor) VisitEntryExpr(e EntryExpr) {
if v.visitEntryExpr != nil {
v.visitEntryExpr(e)
}
}
// NewExprVisitor creates a visitor which only visits expression nodes.
func NewExprVisitor(v func(Expr)) Visitor {
return &baseVisitor{
visitExpr: v,
visitEntryExpr: nil,
}
}
// PostOrderVisit walks the expression graph and calls the visitor in post-order (bottom-up).
func PostOrderVisit(expr Expr, visitor Visitor) {
visit(expr, visitor, postOrder, 0, 0)
}
// PreOrderVisit walks the expression graph and calls the visitor in pre-order (top-down).
func PreOrderVisit(expr Expr, visitor Visitor) {
visit(expr, visitor, preOrder, 0, 0)
}
type visitOrder int
const (
preOrder = iota + 1
postOrder
)
// TODO: consider exposing a way to configure a limit for the max visit depth.
// It's possible that we could want to configure this on the NewExprVisitor()
// and through MatchDescendents() / MaxID().
func visit(expr Expr, visitor Visitor, order visitOrder, depth, maxDepth int) {
if maxDepth > 0 && depth == maxDepth {
return
}
if order == preOrder {
visitor.VisitExpr(expr)
}
switch expr.Kind() {
case CallKind:
c := expr.AsCall()
if c.IsMemberFunction() {
visit(c.Target(), visitor, order, depth+1, maxDepth)
}
for _, arg := range c.Args() {
visit(arg, visitor, order, depth+1, maxDepth)
}
case ComprehensionKind:
c := expr.AsComprehension()
visit(c.IterRange(), visitor, order, depth+1, maxDepth)
visit(c.AccuInit(), visitor, order, depth+1, maxDepth)
visit(c.LoopCondition(), visitor, order, depth+1, maxDepth)
visit(c.LoopStep(), visitor, order, depth+1, maxDepth)
visit(c.Result(), visitor, order, depth+1, maxDepth)
case ListKind:
l := expr.AsList()
for _, elem := range l.Elements() {
visit(elem, visitor, order, depth+1, maxDepth)
}
case MapKind:
m := expr.AsMap()
for _, e := range m.Entries() {
if order == preOrder {
visitor.VisitEntryExpr(e)
}
entry := e.AsMapEntry()
visit(entry.Key(), visitor, order, depth+1, maxDepth)
visit(entry.Value(), visitor, order, depth+1, maxDepth)
if order == postOrder {
visitor.VisitEntryExpr(e)
}
}
case SelectKind:
visit(expr.AsSelect().Operand(), visitor, order, depth+1, maxDepth)
case StructKind:
s := expr.AsStruct()
for _, f := range s.Fields() {
if order == preOrder {
visitor.VisitEntryExpr(f)
}
visit(f.AsStructField().Value(), visitor, order, depth+1, maxDepth)
if order == postOrder {
visitor.VisitEntryExpr(f)
}
}
}
if order == postOrder {
visitor.VisitExpr(expr)
}
}
func matchIsConstantValue(e NavigableExpr) bool {
if e.Kind() == LiteralKind {
return true
}
if e.Kind() == StructKind || e.Kind() == MapKind || e.Kind() == ListKind {
for _, child := range e.Children() {
if !matchIsConstantValue(child) {
return false
}
}
return true
}
return false
}
func newNavigableExpr(ast *AST, parent NavigableExpr, expr Expr, depth int) NavigableExpr {
// Reduce navigable expression nesting by unwrapping the embedded Expr value.
if nav, ok := expr.(*navigableExprImpl); ok {
expr = nav.Expr
}
nav := &navigableExprImpl{
Expr: expr,
depth: depth,
ast: ast,
parent: parent,
createChildren: getChildFactory(expr),
}
return nav
}
type navigableExprImpl struct {
Expr
depth int
ast *AST
parent NavigableExpr
createChildren childFactory
}
func (nav *navigableExprImpl) Parent() (NavigableExpr, bool) {
if nav.parent != nil {
return nav.parent, true
}
return nil, false
}
func (nav *navigableExprImpl) ID() int64 {
return nav.Expr.ID()
}
func (nav *navigableExprImpl) Kind() ExprKind {
return nav.Expr.Kind()
}
func (nav *navigableExprImpl) Type() *types.Type {
return nav.ast.GetType(nav.ID())
}
func (nav *navigableExprImpl) Children() []NavigableExpr {
return nav.createChildren(nav)
}
func (nav *navigableExprImpl) Depth() int {
return nav.depth
}
func (nav *navigableExprImpl) AsCall() CallExpr {
return navigableCallImpl{navigableExprImpl: nav}
}
func (nav *navigableExprImpl) AsComprehension() ComprehensionExpr {
return navigableComprehensionImpl{navigableExprImpl: nav}
}
func (nav *navigableExprImpl) AsIdent() string {
return nav.Expr.AsIdent()
}
func (nav *navigableExprImpl) AsList() ListExpr {
return navigableListImpl{navigableExprImpl: nav}
}
func (nav *navigableExprImpl) AsLiteral() ref.Val {
return nav.Expr.AsLiteral()
}
func (nav *navigableExprImpl) AsMap() MapExpr {
return navigableMapImpl{navigableExprImpl: nav}
}
func (nav *navigableExprImpl) AsSelect() SelectExpr {
return navigableSelectImpl{navigableExprImpl: nav}
}
func (nav *navigableExprImpl) AsStruct() StructExpr {
return navigableStructImpl{navigableExprImpl: nav}
}
func (nav *navigableExprImpl) createChild(e Expr) NavigableExpr {
return newNavigableExpr(nav.ast, nav, e, nav.depth+1)
}
func (nav *navigableExprImpl) isExpr() {}
type navigableCallImpl struct {
*navigableExprImpl
}
func (call navigableCallImpl) FunctionName() string {
return call.Expr.AsCall().FunctionName()
}
func (call navigableCallImpl) IsMemberFunction() bool {
return call.Expr.AsCall().IsMemberFunction()
}
func (call navigableCallImpl) Target() Expr {
t := call.Expr.AsCall().Target()
if t != nil {
return call.createChild(t)
}
return nil
}
func (call navigableCallImpl) Args() []Expr {
args := call.Expr.AsCall().Args()
navArgs := make([]Expr, len(args))
for i, a := range args {
navArgs[i] = call.createChild(a)
}
return navArgs
}
type navigableComprehensionImpl struct {
*navigableExprImpl
}
func (comp navigableComprehensionImpl) IterRange() Expr {
return comp.createChild(comp.Expr.AsComprehension().IterRange())
}
func (comp navigableComprehensionImpl) IterVar() string {
return comp.Expr.AsComprehension().IterVar()
}
func (comp navigableComprehensionImpl) IterVar2() string {
return comp.Expr.AsComprehension().IterVar2()
}
func (comp navigableComprehensionImpl) HasIterVar2() bool {
return comp.Expr.AsComprehension().HasIterVar2()
}
func (comp navigableComprehensionImpl) AccuVar() string {
return comp.Expr.AsComprehension().AccuVar()
}
func (comp navigableComprehensionImpl) AccuInit() Expr {
return comp.createChild(comp.Expr.AsComprehension().AccuInit())
}
func (comp navigableComprehensionImpl) LoopCondition() Expr {
return comp.createChild(comp.Expr.AsComprehension().LoopCondition())
}
func (comp navigableComprehensionImpl) LoopStep() Expr {
return comp.createChild(comp.Expr.AsComprehension().LoopStep())
}
func (comp navigableComprehensionImpl) Result() Expr {
return comp.createChild(comp.Expr.AsComprehension().Result())
}
type navigableListImpl struct {
*navigableExprImpl
}
func (l navigableListImpl) Elements() []Expr {
pbElems := l.Expr.AsList().Elements()
elems := make([]Expr, len(pbElems))
for i := 0; i < len(pbElems); i++ {
elems[i] = l.createChild(pbElems[i])
}
return elems
}
func (l navigableListImpl) IsOptional(index int32) bool {
return l.Expr.AsList().IsOptional(index)
}
func (l navigableListImpl) OptionalIndices() []int32 {
return l.Expr.AsList().OptionalIndices()
}
func (l navigableListImpl) Size() int {
return l.Expr.AsList().Size()
}
type navigableMapImpl struct {
*navigableExprImpl
}
func (m navigableMapImpl) Entries() []EntryExpr {
mapExpr := m.Expr.AsMap()
entries := make([]EntryExpr, len(mapExpr.Entries()))
for i, e := range mapExpr.Entries() {
entry := e.AsMapEntry()
entries[i] = &entryExpr{
id: e.ID(),
entryExprKindCase: navigableEntryImpl{
key: m.createChild(entry.Key()),
val: m.createChild(entry.Value()),
isOpt: entry.IsOptional(),
},
}
}
return entries
}
func (m navigableMapImpl) Size() int {
return m.Expr.AsMap().Size()
}
type navigableEntryImpl struct {
key NavigableExpr
val NavigableExpr
isOpt bool
}
func (e navigableEntryImpl) Kind() EntryExprKind {
return MapEntryKind
}
func (e navigableEntryImpl) Key() Expr {
return e.key
}
func (e navigableEntryImpl) Value() Expr {
return e.val
}
func (e navigableEntryImpl) IsOptional() bool {
return e.isOpt
}
func (e navigableEntryImpl) renumberIDs(IDGenerator) {}
func (e navigableEntryImpl) isEntryExpr() {}
type navigableSelectImpl struct {
*navigableExprImpl
}
func (sel navigableSelectImpl) FieldName() string {
return sel.Expr.AsSelect().FieldName()
}
func (sel navigableSelectImpl) IsTestOnly() bool {
return sel.Expr.AsSelect().IsTestOnly()
}
func (sel navigableSelectImpl) Operand() Expr {
return sel.createChild(sel.Expr.AsSelect().Operand())
}
type navigableStructImpl struct {
*navigableExprImpl
}
func (s navigableStructImpl) TypeName() string {
return s.Expr.AsStruct().TypeName()
}
func (s navigableStructImpl) Fields() []EntryExpr {
fieldInits := s.Expr.AsStruct().Fields()
fields := make([]EntryExpr, len(fieldInits))
for i, f := range fieldInits {
field := f.AsStructField()
fields[i] = &entryExpr{
id: f.ID(),
entryExprKindCase: navigableFieldImpl{
name: field.Name(),
val: s.createChild(field.Value()),
isOpt: field.IsOptional(),
},
}
}
return fields
}
type navigableFieldImpl struct {
name string
val NavigableExpr
isOpt bool
}
func (f navigableFieldImpl) Kind() EntryExprKind {
return StructFieldKind
}
func (f navigableFieldImpl) Name() string {
return f.name
}
func (f navigableFieldImpl) Value() Expr {
return f.val
}
func (f navigableFieldImpl) IsOptional() bool {
return f.isOpt
}
func (f navigableFieldImpl) renumberIDs(IDGenerator) {}
func (f navigableFieldImpl) isEntryExpr() {}
func getChildFactory(expr Expr) childFactory {
if expr == nil {
return noopFactory
}
switch expr.Kind() {
case LiteralKind:
return noopFactory
case IdentKind:
return noopFactory
case SelectKind:
return selectFactory
case CallKind:
return callArgFactory
case ListKind:
return listElemFactory
case MapKind:
return mapEntryFactory
case StructKind:
return structEntryFactory
case ComprehensionKind:
return comprehensionFactory
default:
return noopFactory
}
}
type childFactory func(*navigableExprImpl) []NavigableExpr
func noopFactory(*navigableExprImpl) []NavigableExpr {
return nil
}
func selectFactory(nav *navigableExprImpl) []NavigableExpr {
return []NavigableExpr{nav.createChild(nav.AsSelect().Operand())}
}
func callArgFactory(nav *navigableExprImpl) []NavigableExpr {
call := nav.Expr.AsCall()
argCount := len(call.Args())
if call.IsMemberFunction() {
argCount++
}
navExprs := make([]NavigableExpr, argCount)
i := 0
if call.IsMemberFunction() {
navExprs[i] = nav.createChild(call.Target())
i++
}
for _, arg := range call.Args() {
navExprs[i] = nav.createChild(arg)
i++
}
return navExprs
}
func listElemFactory(nav *navigableExprImpl) []NavigableExpr {
l := nav.Expr.AsList()
navExprs := make([]NavigableExpr, len(l.Elements()))
for i, e := range l.Elements() {
navExprs[i] = nav.createChild(e)
}
return navExprs
}
func structEntryFactory(nav *navigableExprImpl) []NavigableExpr {
s := nav.Expr.AsStruct()
entries := make([]NavigableExpr, len(s.Fields()))
for i, e := range s.Fields() {
f := e.AsStructField()
entries[i] = nav.createChild(f.Value())
}
return entries
}
func mapEntryFactory(nav *navigableExprImpl) []NavigableExpr {
m := nav.Expr.AsMap()
entries := make([]NavigableExpr, len(m.Entries())*2)
j := 0
for _, e := range m.Entries() {
mapEntry := e.AsMapEntry()
entries[j] = nav.createChild(mapEntry.Key())
entries[j+1] = nav.createChild(mapEntry.Value())
j += 2
}
return entries
}
func comprehensionFactory(nav *navigableExprImpl) []NavigableExpr {
compre := nav.Expr.AsComprehension()
return []NavigableExpr{
nav.createChild(compre.IterRange()),
nav.createChild(compre.AccuInit()),
nav.createChild(compre.LoopCondition()),
nav.createChild(compre.LoopStep()),
nav.createChild(compre.Result()),
}
}
// Copyright 2018 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Package containers defines types and functions for resolving qualified names within a namespace
// or type provided to CEL.
package containers
import (
"fmt"
"strings"
"unicode"
"github.com/google/cel-go/common/ast"
)
var (
// DefaultContainer has an empty container name.
DefaultContainer *Container = nil
// Empty map to search for aliases when needed.
noAliases = make(map[string]string)
)
// NewContainer creates a new Container with the fully-qualified name.
func NewContainer(opts ...ContainerOption) (*Container, error) {
var c *Container
var err error
for _, opt := range opts {
c, err = opt(c)
if err != nil {
return nil, err
}
}
return c, nil
}
// Container holds a reference to an optional qualified container name and set of aliases.
//
// The program container can be used to simplify variable, function, and type specification within
// CEL programs and behaves more or less like a C++ namespace. See ResolveCandidateNames for more
// details.
type Container struct {
name string
aliases map[string]string
}
// Extend creates a new Container with the existing settings and applies a series of
// ContainerOptions to further configure the new container.
func (c *Container) Extend(opts ...ContainerOption) (*Container, error) {
if c == nil {
return NewContainer(opts...)
}
// Copy the name and aliases of the existing container.
ext := &Container{name: c.Name()}
if len(c.AliasSet()) > 0 {
aliasSet := make(map[string]string, len(c.AliasSet()))
for k, v := range c.AliasSet() {
aliasSet[k] = v
}
ext.aliases = aliasSet
}
// Apply the new options to the container.
var err error
for _, opt := range opts {
ext, err = opt(ext)
if err != nil {
return nil, err
}
}
return ext, nil
}
// Name returns the fully-qualified name of the container.
//
// The name may conceptually be a namespace, package, or type.
func (c *Container) Name() string {
if c == nil {
return ""
}
return c.name
}
// ResolveCandidateNames returns the candidates name of namespaced identifiers in C++ resolution
// order.
//
// Names which shadow other names are returned first. If a name includes a leading dot ('.'),
// the name is treated as an absolute identifier which cannot be shadowed.
//
// Given a container name a.b.c.M.N and a type name R.s, this will deliver in order:
//
// a.b.c.M.N.R.s
// a.b.c.M.R.s
// a.b.c.R.s
// a.b.R.s
// a.R.s
// R.s
//
// If aliases or abbreviations are configured for the container, then alias names will take
// precedence over containerized names.
func (c *Container) ResolveCandidateNames(name string) []string {
if strings.HasPrefix(name, ".") {
qn := name[1:]
alias, isAlias := c.findAlias(qn)
if isAlias {
return []string{alias}
}
return []string{qn}
}
alias, isAlias := c.findAlias(name)
if isAlias {
return []string{alias}
}
if c.Name() == "" {
return []string{name}
}
nextCont := c.Name()
candidates := []string{nextCont + "." + name}
for i := strings.LastIndex(nextCont, "."); i >= 0; i = strings.LastIndex(nextCont, ".") {
nextCont = nextCont[:i]
candidates = append(candidates, nextCont+"."+name)
}
return append(candidates, name)
}
// AliasSet returns the alias to fully-qualified name mapping stored in the container.
func (c *Container) AliasSet() map[string]string {
if c == nil || c.aliases == nil {
return noAliases
}
return c.aliases
}
// findAlias takes a name as input and returns an alias expansion if one exists.
//
// If the name is qualified, the first component of the qualified name is checked against known
// aliases. Any alias that is found in a qualified name is expanded in the result:
//
// alias: R -> my.alias.R
// name: R.S.T
// output: my.alias.R.S.T
//
// Note, the name must not have a leading dot.
func (c *Container) findAlias(name string) (string, bool) {
// If an alias exists for the name, ensure it is searched last.
simple := name
qualifier := ""
dot := strings.Index(name, ".")
if dot >= 0 {
simple = name[0:dot]
qualifier = name[dot:]
}
alias, found := c.AliasSet()[simple]
if !found {
return "", false
}
return alias + qualifier, true
}
// ContainerOption specifies a functional configuration option for a Container.
//
// Note, ContainerOption implementations must be able to handle nil container inputs.
type ContainerOption func(*Container) (*Container, error)
// Abbrevs configures a set of simple names as abbreviations for fully-qualified names.
//
// An abbreviation (abbrev for short) is a simple name that expands to a fully-qualified name.
// Abbreviations can be useful when working with variables, functions, and especially types from
// multiple namespaces:
//
// // CEL object construction
// qual.pkg.version.ObjTypeName{
// field: alt.container.ver.FieldTypeName{value: ...}
// }
//
// Only one the qualified names above may be used as the CEL container, so at least one of these
// references must be a long qualified name within an otherwise short CEL program. Using the
// following abbreviations, the program becomes much simpler:
//
// // CEL Go option
// Abbrevs("qual.pkg.version.ObjTypeName", "alt.container.ver.FieldTypeName")
// // Simplified Object construction
// ObjTypeName{field: FieldTypeName{value: ...}}
//
// There are a few rules for the qualified names and the simple abbreviations generated from them:
// - Qualified names must be dot-delimited, e.g. `package.subpkg.name`.
// - The last element in the qualified name is the abbreviation.
// - Abbreviations must not collide with each other.
// - The abbreviation must not collide with unqualified names in use.
//
// Abbreviations are distinct from container-based references in the following important ways:
// - Abbreviations must expand to a fully-qualified name.
// - Expanded abbreviations do not participate in namespace resolution.
// - Abbreviation expansion is done instead of the container search for a matching identifier.
// - Containers follow C++ namespace resolution rules with searches from the most qualified name
// to the least qualified name.
// - Container references within the CEL program may be relative, and are resolved to fully
// qualified names at either type-check time or program plan time, whichever comes first.
//
// If there is ever a case where an identifier could be in both the container and as an
// abbreviation, the abbreviation wins as this will ensure that the meaning of a program is
// preserved between compilations even as the container evolves.
func Abbrevs(qualifiedNames ...string) ContainerOption {
return func(c *Container) (*Container, error) {
for _, qn := range qualifiedNames {
qn = strings.TrimSpace(qn)
for _, r := range qn {
if !isIdentifierChar(r) {
return nil, fmt.Errorf(
"invalid qualified name: %s, wanted name of the form 'qualified.name'", qn)
}
}
ind := strings.LastIndex(qn, ".")
if ind <= 0 || ind >= len(qn)-1 {
return nil, fmt.Errorf(
"invalid qualified name: %s, wanted name of the form 'qualified.name'", qn)
}
alias := qn[ind+1:]
var err error
c, err = aliasAs("abbreviation", qn, alias)(c)
if err != nil {
return nil, err
}
}
return c, nil
}
}
// Alias associates a fully-qualified name with a user-defined alias.
//
// In general, Abbrevs is preferred to Alias since the names generated from the Abbrevs option
// are more easily traced back to source code. The Alias option is useful for propagating alias
// configuration from one Container instance to another, and may also be useful for remapping
// poorly chosen protobuf message / package names.
//
// Note: all of the rules that apply to Abbrevs also apply to Alias.
func Alias(qualifiedName, alias string) ContainerOption {
return aliasAs("alias", qualifiedName, alias)
}
func aliasAs(kind, qualifiedName, alias string) ContainerOption {
return func(c *Container) (*Container, error) {
if len(alias) == 0 || strings.Contains(alias, ".") {
return nil, fmt.Errorf(
"%s must be non-empty and simple (not qualified): %s=%s", kind, kind, alias)
}
if qualifiedName[0:1] == "." {
return nil, fmt.Errorf("qualified name must not begin with a leading '.': %s",
qualifiedName)
}
ind := strings.LastIndex(qualifiedName, ".")
if ind <= 0 || ind == len(qualifiedName)-1 {
return nil, fmt.Errorf("%s must refer to a valid qualified name: %s",
kind, qualifiedName)
}
aliasRef, found := c.AliasSet()[alias]
if found {
return nil, fmt.Errorf(
"%s collides with existing reference: name=%s, %s=%s, existing=%s",
kind, qualifiedName, kind, alias, aliasRef)
}
if strings.HasPrefix(c.Name(), alias+".") || c.Name() == alias {
return nil, fmt.Errorf(
"%s collides with container name: name=%s, %s=%s, container=%s",
kind, qualifiedName, kind, alias, c.Name())
}
if c == nil {
c = &Container{}
}
if c.aliases == nil {
c.aliases = make(map[string]string)
}
c.aliases[alias] = qualifiedName
return c, nil
}
}
func isIdentifierChar(r rune) bool {
return r <= unicode.MaxASCII && (r == '.' || r == '_' || unicode.IsLetter(r) || unicode.IsNumber(r))
}
// Name sets the fully-qualified name of the Container.
func Name(name string) ContainerOption {
return func(c *Container) (*Container, error) {
if len(name) > 0 && name[0:1] == "." {
return nil, fmt.Errorf("container name must not contain a leading '.': %s", name)
}
if c.Name() == name {
return c, nil
}
if c == nil {
return &Container{name: name}, nil
}
c.name = name
return c, nil
}
}
// ToQualifiedName converts an expression AST into a qualified name if possible, with a boolean
// 'found' value that indicates if the conversion is successful.
func ToQualifiedName(e ast.Expr) (string, bool) {
switch e.Kind() {
case ast.IdentKind:
id := e.AsIdent()
return id, true
case ast.SelectKind:
sel := e.AsSelect()
// Test only expressions are not valid as qualified names.
if sel.IsTestOnly() {
return "", false
}
if qual, found := ToQualifiedName(sel.Operand()); found {
return qual + "." + sel.FieldName(), true
}
}
return "", false
}
// Copyright 2018 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Package debug provides tools to print a parsed expression graph and
// adorn each expression element with additional metadata.
package debug
import (
"bytes"
"fmt"
"strconv"
"strings"
"github.com/google/cel-go/common/ast"
"github.com/google/cel-go/common/types"
"github.com/google/cel-go/common/types/ref"
)
// Adorner returns debug metadata that will be tacked on to the string
// representation of an expression.
type Adorner interface {
// GetMetadata for the input context.
GetMetadata(ctx any) string
}
// Writer manages writing expressions to an internal string.
type Writer interface {
fmt.Stringer
// Buffer pushes an expression into an internal queue of expressions to
// write to a string.
Buffer(e ast.Expr)
}
type emptyDebugAdorner struct {
}
var emptyAdorner Adorner = &emptyDebugAdorner{}
func (a *emptyDebugAdorner) GetMetadata(e any) string {
return ""
}
// ToDebugString gives the unadorned string representation of the Expr.
func ToDebugString(e ast.Expr) string {
return ToAdornedDebugString(e, emptyAdorner)
}
// ToAdornedDebugString gives the adorned string representation of the Expr.
func ToAdornedDebugString(e ast.Expr, adorner Adorner) string {
w := newDebugWriter(adorner)
w.Buffer(e)
return w.String()
}
// debugWriter is used to print out pretty-printed debug strings.
type debugWriter struct {
adorner Adorner
buffer bytes.Buffer
indent int
lineStart bool
}
func newDebugWriter(a Adorner) *debugWriter {
return &debugWriter{
adorner: a,
indent: 0,
lineStart: true,
}
}
func (w *debugWriter) Buffer(e ast.Expr) {
if e == nil {
return
}
switch e.Kind() {
case ast.LiteralKind:
w.append(formatLiteral(e.AsLiteral()))
case ast.IdentKind:
w.append(e.AsIdent())
case ast.SelectKind:
w.appendSelect(e.AsSelect())
case ast.CallKind:
w.appendCall(e.AsCall())
case ast.ListKind:
w.appendList(e.AsList())
case ast.MapKind:
w.appendMap(e.AsMap())
case ast.StructKind:
w.appendStruct(e.AsStruct())
case ast.ComprehensionKind:
w.appendComprehension(e.AsComprehension())
}
w.adorn(e)
}
func (w *debugWriter) appendSelect(sel ast.SelectExpr) {
w.Buffer(sel.Operand())
w.append(".")
w.append(sel.FieldName())
if sel.IsTestOnly() {
w.append("~test-only~")
}
}
func (w *debugWriter) appendCall(call ast.CallExpr) {
if call.IsMemberFunction() {
w.Buffer(call.Target())
w.append(".")
}
w.append(call.FunctionName())
w.append("(")
if len(call.Args()) > 0 {
w.addIndent()
w.appendLine()
for i, arg := range call.Args() {
if i > 0 {
w.append(",")
w.appendLine()
}
w.Buffer(arg)
}
w.removeIndent()
w.appendLine()
}
w.append(")")
}
func (w *debugWriter) appendList(list ast.ListExpr) {
w.append("[")
if len(list.Elements()) > 0 {
w.appendLine()
w.addIndent()
for i, elem := range list.Elements() {
if i > 0 {
w.append(",")
w.appendLine()
}
w.Buffer(elem)
}
w.removeIndent()
w.appendLine()
}
w.append("]")
}
func (w *debugWriter) appendStruct(obj ast.StructExpr) {
w.append(obj.TypeName())
w.append("{")
if len(obj.Fields()) > 0 {
w.appendLine()
w.addIndent()
for i, f := range obj.Fields() {
field := f.AsStructField()
if i > 0 {
w.append(",")
w.appendLine()
}
if field.IsOptional() {
w.append("?")
}
w.append(field.Name())
w.append(":")
w.Buffer(field.Value())
w.adorn(f)
}
w.removeIndent()
w.appendLine()
}
w.append("}")
}
func (w *debugWriter) appendMap(m ast.MapExpr) {
w.append("{")
if m.Size() > 0 {
w.appendLine()
w.addIndent()
for i, e := range m.Entries() {
entry := e.AsMapEntry()
if i > 0 {
w.append(",")
w.appendLine()
}
if entry.IsOptional() {
w.append("?")
}
w.Buffer(entry.Key())
w.append(":")
w.Buffer(entry.Value())
w.adorn(e)
}
w.removeIndent()
w.appendLine()
}
w.append("}")
}
func (w *debugWriter) appendComprehension(comprehension ast.ComprehensionExpr) {
w.append("__comprehension__(")
w.addIndent()
w.appendLine()
w.append("// Variable")
w.appendLine()
w.append(comprehension.IterVar())
w.append(",")
w.appendLine()
if comprehension.HasIterVar2() {
w.append(comprehension.IterVar2())
w.append(",")
w.appendLine()
}
w.append("// Target")
w.appendLine()
w.Buffer(comprehension.IterRange())
w.append(",")
w.appendLine()
w.append("// Accumulator")
w.appendLine()
w.append(comprehension.AccuVar())
w.append(",")
w.appendLine()
w.append("// Init")
w.appendLine()
w.Buffer(comprehension.AccuInit())
w.append(",")
w.appendLine()
w.append("// LoopCondition")
w.appendLine()
w.Buffer(comprehension.LoopCondition())
w.append(",")
w.appendLine()
w.append("// LoopStep")
w.appendLine()
w.Buffer(comprehension.LoopStep())
w.append(",")
w.appendLine()
w.append("// Result")
w.appendLine()
w.Buffer(comprehension.Result())
w.append(")")
w.removeIndent()
}
func formatLiteral(c ref.Val) string {
switch v := c.(type) {
case types.Bool:
return fmt.Sprintf("%t", v)
case types.Bytes:
return fmt.Sprintf("b%s", strconv.Quote(string(v)))
case types.Double:
return fmt.Sprintf("%v", float64(v))
case types.Int:
return fmt.Sprintf("%d", int64(v))
case types.String:
return strconv.Quote(string(v))
case types.Uint:
return fmt.Sprintf("%du", uint64(v))
case types.Null:
return "null"
default:
panic("Unknown constant type")
}
}
func (w *debugWriter) append(s string) {
w.doIndent()
w.buffer.WriteString(s)
}
func (w *debugWriter) appendFormat(f string, args ...any) {
w.append(fmt.Sprintf(f, args...))
}
func (w *debugWriter) doIndent() {
if w.lineStart {
w.lineStart = false
w.buffer.WriteString(strings.Repeat(" ", w.indent))
}
}
func (w *debugWriter) adorn(e any) {
w.append(w.adorner.GetMetadata(e))
}
func (w *debugWriter) appendLine() {
w.buffer.WriteString("\n")
w.lineStart = true
}
func (w *debugWriter) addIndent() {
w.indent++
}
func (w *debugWriter) removeIndent() {
w.indent--
if w.indent < 0 {
panic("negative indent")
}
}
func (w *debugWriter) String() string {
return w.buffer.String()
}
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Package decls contains function and variable declaration structs and helper methods.
package decls
import (
"fmt"
"strings"
chkdecls "github.com/google/cel-go/checker/decls"
"github.com/google/cel-go/common"
"github.com/google/cel-go/common/functions"
"github.com/google/cel-go/common/operators"
"github.com/google/cel-go/common/types"
"github.com/google/cel-go/common/types/ref"
exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1"
)
// NewFunction creates a new function declaration with a set of function options to configure overloads
// and function definitions (implementations).
//
// Functions are checked for name collisions and singleton redefinition.
func NewFunction(name string, opts ...FunctionOpt) (*FunctionDecl, error) {
fn := &FunctionDecl{
name: name,
overloads: map[string]*OverloadDecl{},
overloadOrdinals: []string{},
}
var err error
for _, opt := range opts {
fn, err = opt(fn)
if err != nil {
return nil, err
}
}
if len(fn.overloads) == 0 {
return nil, fmt.Errorf("function %s must have at least one overload", name)
}
return fn, nil
}
// FunctionDecl defines a function name, overload set, and optionally a singleton definition for all
// overload instances.
type FunctionDecl struct {
name string
doc string
// overloads associated with the function name.
overloads map[string]*OverloadDecl
// singleton implementation of the function for all overloads.
//
// If this option is set, an error will occur if any overloads specify a per-overload implementation
// or if another function with the same name attempts to redefine the singleton.
singleton *functions.Overload
// disableTypeGuards is a performance optimization to disable detailed runtime type checks which could
// add overhead on common operations. Setting this option true leaves error checks and argument checks
// intact.
disableTypeGuards bool
// state indicates that the binding should be provided as a declaration, as a runtime binding, or both.
state declarationState
// overloadOrdinals indicates the order in which the overload was declared.
overloadOrdinals []string
}
type declarationState int
const (
declarationStateUnset declarationState = iota
declarationDisabled
declarationEnabled
)
// Documentation generates documentation about the Function and its overloads as a common.Doc object.
func (f *FunctionDecl) Documentation() *common.Doc {
if f == nil {
return nil
}
children := make([]*common.Doc, len(f.OverloadDecls()))
for i, o := range f.OverloadDecls() {
var examples []*common.Doc
for _, ex := range o.Examples() {
examples = append(examples, common.NewExampleDoc(ex))
}
od := common.NewOverloadDoc(o.ID(), formatSignature(f.Name(), o), examples...)
children[i] = od
}
return common.NewFunctionDoc(
f.Name(),
f.Description(),
children...)
}
// Name returns the function name in human-readable terms, e.g. 'contains' of 'math.least'
func (f *FunctionDecl) Name() string {
if f == nil {
return ""
}
return f.name
}
// Description provides an overview of the function's purpose.
//
// Usage examples should be included on specific overloads.
func (f *FunctionDecl) Description() string {
if f == nil {
return ""
}
return f.doc
}
// IsDeclarationDisabled indicates that the function implementation should be added to the dispatcher, but the
// declaration should not be exposed for use in expressions.
func (f *FunctionDecl) IsDeclarationDisabled() bool {
if f == nil {
return true
}
return f.state == declarationDisabled
}
// Merge combines an existing function declaration with another.
//
// If a function is extended, by say adding new overloads to an existing function, then it is merged with the
// prior definition of the function at which point its overloads must not collide with pre-existing overloads
// and its bindings (singleton, or per-overload) must not conflict with previous definitions either.
func (f *FunctionDecl) Merge(other *FunctionDecl) (*FunctionDecl, error) {
if f == other {
return f, nil
}
if f == nil || other == nil || f.Name() != other.Name() {
return nil, fmt.Errorf("cannot merge unrelated functions. %q and %q", f.Name(), other.Name())
}
merged := &FunctionDecl{
name: f.Name(),
overloads: make(map[string]*OverloadDecl, len(f.overloads)),
singleton: f.singleton,
overloadOrdinals: make([]string, len(f.overloads)),
// if one function is expecting type-guards and the other is not, then they
// must not be disabled.
disableTypeGuards: f.disableTypeGuards && other.disableTypeGuards,
// default to the current functions declaration state.
state: f.state,
doc: f.doc,
}
// If the other state indicates that the declaration should be explicitly enabled or
// disabled, then update the merged state with the most recent value.
if other.state != declarationStateUnset {
merged.state = other.state
}
// Allow for non-empty overrides of documentation
if len(other.doc) != 0 && f.doc != other.doc {
merged.doc = other.doc
}
// baseline copy of the overloads and their ordinals
copy(merged.overloadOrdinals, f.overloadOrdinals)
for oID, o := range f.overloads {
merged.overloads[oID] = o
}
// overloads and their ordinals are added from the left
for _, oID := range other.overloadOrdinals {
o := other.overloads[oID]
err := merged.AddOverload(o)
if err != nil {
return nil, fmt.Errorf("function declaration merge failed: %v", err)
}
}
if other.singleton != nil {
if merged.singleton != nil && merged.singleton != other.singleton {
return nil, fmt.Errorf("function already has a singleton binding: %s", f.Name())
}
merged.singleton = other.singleton
}
return merged, nil
}
// FunctionSubsetter subsets a function declaration or returns nil and false if the function
// subset was empty.
type FunctionSubsetter func(fn *FunctionDecl) (*FunctionDecl, bool)
// OverloadSelector selects an overload associated with a given function when it returns true.
//
// Used in combination with the Subset method.
type OverloadSelector func(overload *OverloadDecl) bool
// IncludeOverloads defines an OverloadSelector which allow-lists a set of overloads by their ids.
func IncludeOverloads(overloadIDs ...string) OverloadSelector {
return func(overload *OverloadDecl) bool {
for _, oID := range overloadIDs {
if overload.id == oID {
return true
}
}
return false
}
}
// ExcludeOverloads defines an OverloadSelector which deny-lists a set of overloads by their ids.
func ExcludeOverloads(overloadIDs ...string) OverloadSelector {
return func(overload *OverloadDecl) bool {
for _, oID := range overloadIDs {
if overload.id == oID {
return false
}
}
return true
}
}
// Subset returns a new function declaration which contains only the overloads with the specified IDs.
// If the subset function contains no overloads, then nil is returned to indicate the function is not
// functional.
func (f *FunctionDecl) Subset(selector OverloadSelector) *FunctionDecl {
if f == nil {
return nil
}
overloads := make(map[string]*OverloadDecl)
overloadOrdinals := make([]string, 0, len(f.overloadOrdinals))
for _, oID := range f.overloadOrdinals {
overload := f.overloads[oID]
if selector(overload) {
overloads[oID] = overload
overloadOrdinals = append(overloadOrdinals, oID)
}
}
if len(overloads) == 0 {
return nil
}
subset := &FunctionDecl{
name: f.Name(),
doc: f.doc,
overloads: overloads,
singleton: f.singleton,
disableTypeGuards: f.disableTypeGuards,
state: f.state,
overloadOrdinals: overloadOrdinals,
}
return subset
}
// AddOverload ensures that the new overload does not collide with an existing overload signature;
// however, if the function signatures are identical, the implementation may be rewritten as its
// difficult to compare functions by object identity.
func (f *FunctionDecl) AddOverload(overload *OverloadDecl) error {
if f == nil {
return fmt.Errorf("nil function cannot add overload: %s", overload.ID())
}
if overload == nil {
return fmt.Errorf("cannot add nil overload to funciton: %s", f.Name())
}
for oID, o := range f.overloads {
if oID != overload.ID() && o.SignatureOverlaps(overload) {
return fmt.Errorf("overload signature collision in function %s: %s collides with %s", f.Name(), oID, overload.ID())
}
if oID == overload.ID() {
if o.SignatureEquals(overload) && o.IsNonStrict() == overload.IsNonStrict() {
// Allow redefinition of an overload implementation so long as the signatures match.
if overload.hasBinding() {
f.overloads[oID] = overload
}
// Allow redefinition of the doc string.
if len(overload.doc) != 0 && o.doc != overload.doc {
o.doc = overload.doc
}
return nil
}
return fmt.Errorf("overload redefinition in function. %s: %s has multiple definitions", f.Name(), oID)
}
if overload.HasLateBinding() != o.HasLateBinding() {
return fmt.Errorf("overload with late binding cannot be added to function %s: cannot mix late and non-late bindings", f.Name())
}
}
f.overloadOrdinals = append(f.overloadOrdinals, overload.ID())
f.overloads[overload.ID()] = overload
return nil
}
// OverloadDecls returns the overload declarations in the order in which they were declared.
func (f *FunctionDecl) OverloadDecls() []*OverloadDecl {
var emptySet []*OverloadDecl
if f == nil {
return emptySet
}
overloads := make([]*OverloadDecl, 0, len(f.overloads))
for _, oID := range f.overloadOrdinals {
overloads = append(overloads, f.overloads[oID])
}
return overloads
}
// HasLateBinding returns true if the function has late bindings. A function cannot mix late bindings with other bindings.
func (f *FunctionDecl) HasLateBinding() bool {
if f == nil {
return false
}
for _, oID := range f.overloadOrdinals {
if f.overloads[oID].HasLateBinding() {
return true
}
}
return false
}
// Bindings produces a set of function bindings, if any are defined.
func (f *FunctionDecl) Bindings() ([]*functions.Overload, error) {
var emptySet []*functions.Overload
if f == nil {
return emptySet, nil
}
overloads := []*functions.Overload{}
nonStrict := false
hasLateBinding := false
for _, oID := range f.overloadOrdinals {
o := f.overloads[oID]
hasLateBinding = hasLateBinding || o.HasLateBinding()
if o.hasBinding() {
overload := &functions.Overload{
Operator: o.ID(),
Unary: o.guardedUnaryOp(f.Name(), f.disableTypeGuards),
Binary: o.guardedBinaryOp(f.Name(), f.disableTypeGuards),
Function: o.guardedFunctionOp(f.Name(), f.disableTypeGuards),
OperandTrait: o.OperandTrait(),
NonStrict: o.IsNonStrict(),
}
overloads = append(overloads, overload)
nonStrict = nonStrict || o.IsNonStrict()
}
}
if f.singleton != nil {
if len(overloads) != 0 {
return nil, fmt.Errorf("singleton function incompatible with specialized overloads: %s", f.Name())
}
if hasLateBinding {
return nil, fmt.Errorf("singleton function incompatible with late bindings: %s", f.Name())
}
overloads = []*functions.Overload{
{
Operator: f.Name(),
Unary: f.singleton.Unary,
Binary: f.singleton.Binary,
Function: f.singleton.Function,
OperandTrait: f.singleton.OperandTrait,
},
}
// fall-through to return single overload case.
}
if len(overloads) == 0 {
return overloads, nil
}
// Single overload. Replicate an entry for it using the function name as well.
if len(overloads) == 1 {
if overloads[0].Operator == f.Name() {
return overloads, nil
}
return append(overloads, &functions.Overload{
Operator: f.Name(),
Unary: overloads[0].Unary,
Binary: overloads[0].Binary,
Function: overloads[0].Function,
NonStrict: overloads[0].NonStrict,
OperandTrait: overloads[0].OperandTrait,
}), nil
}
// All of the defined overloads are wrapped into a top-level function which
// performs dynamic dispatch to the proper overload based on the argument types.
bindings := append([]*functions.Overload{}, overloads...)
funcDispatch := func(args ...ref.Val) ref.Val {
for _, oID := range f.overloadOrdinals {
o := f.overloads[oID]
// During dynamic dispatch over multiple functions, signature agreement checks
// are preserved in order to assist with the function resolution step.
switch len(args) {
case 1:
if o.unaryOp != nil && o.matchesRuntimeSignature(f.disableTypeGuards, args...) {
return o.unaryOp(args[0])
}
case 2:
if o.binaryOp != nil && o.matchesRuntimeSignature(f.disableTypeGuards, args...) {
return o.binaryOp(args[0], args[1])
}
}
if o.functionOp != nil && o.matchesRuntimeSignature(f.disableTypeGuards, args...) {
return o.functionOp(args...)
}
// eventually this will fall through to the noSuchOverload below.
}
return MaybeNoSuchOverload(f.Name(), args...)
}
function := &functions.Overload{
Operator: f.Name(),
Function: funcDispatch,
NonStrict: nonStrict,
}
return append(bindings, function), nil
}
// MaybeNoSuchOverload determines whether to propagate an error if one is provided as an argument, or
// to return an unknown set, or to produce a new error for a missing function signature.
func MaybeNoSuchOverload(funcName string, args ...ref.Val) ref.Val {
argTypes := make([]string, len(args))
var unk *types.Unknown = nil
for i, arg := range args {
if types.IsError(arg) {
return arg
}
if types.IsUnknown(arg) {
unk = types.MergeUnknowns(arg.(*types.Unknown), unk)
}
argTypes[i] = arg.Type().TypeName()
}
if unk != nil {
return unk
}
signature := strings.Join(argTypes, ", ")
return types.NewErr("no such overload: %s(%s)", funcName, signature)
}
// FunctionOpt defines a functional option for mutating a function declaration.
type FunctionOpt func(*FunctionDecl) (*FunctionDecl, error)
// FunctionDocs configures documentation from a list of strings separated by newlines.
func FunctionDocs(docs ...string) FunctionOpt {
return func(fn *FunctionDecl) (*FunctionDecl, error) {
fn.doc = common.MultilineDescription(docs...)
return fn, nil
}
}
// DisableTypeGuards disables automatically generated function invocation guards on direct overload calls.
// Type guards remain on during dynamic dispatch for parsed-only expressions.
func DisableTypeGuards(value bool) FunctionOpt {
return func(fn *FunctionDecl) (*FunctionDecl, error) {
fn.disableTypeGuards = value
return fn, nil
}
}
// DisableDeclaration indicates that the function declaration should be disabled, but the runtime function
// binding should be provided. Marking a function as runtime-only is a safe way to manage deprecations
// of function declarations while still preserving the runtime behavior for previously compiled expressions.
func DisableDeclaration(value bool) FunctionOpt {
return func(fn *FunctionDecl) (*FunctionDecl, error) {
if value {
fn.state = declarationDisabled
} else {
fn.state = declarationEnabled
}
return fn, nil
}
}
// SingletonUnaryBinding creates a singleton function definition to be used for all function overloads.
//
// Note, this approach works well if operand is expected to have a specific trait which it implements,
// e.g. traits.ContainerType. Otherwise, prefer per-overload function bindings.
func SingletonUnaryBinding(fn functions.UnaryOp, traits ...int) FunctionOpt {
trait := 0
for _, t := range traits {
trait = trait | t
}
return func(f *FunctionDecl) (*FunctionDecl, error) {
if f.singleton != nil {
return nil, fmt.Errorf("function already has a singleton binding: %s", f.Name())
}
f.singleton = &functions.Overload{
Operator: f.Name(),
Unary: fn,
OperandTrait: trait,
}
return f, nil
}
}
// SingletonBinaryBinding creates a singleton function definition to be used with all function overloads.
//
// Note, this approach works well if operand is expected to have a specific trait which it implements,
// e.g. traits.ContainerType. Otherwise, prefer per-overload function bindings.
func SingletonBinaryBinding(fn functions.BinaryOp, traits ...int) FunctionOpt {
trait := 0
for _, t := range traits {
trait = trait | t
}
return func(f *FunctionDecl) (*FunctionDecl, error) {
if f.singleton != nil {
return nil, fmt.Errorf("function already has a singleton binding: %s", f.Name())
}
f.singleton = &functions.Overload{
Operator: f.Name(),
Binary: fn,
OperandTrait: trait,
}
return f, nil
}
}
// SingletonFunctionBinding creates a singleton function definition to be used with all function overloads.
//
// Note, this approach works well if operand is expected to have a specific trait which it implements,
// e.g. traits.ContainerType. Otherwise, prefer per-overload function bindings.
func SingletonFunctionBinding(fn functions.FunctionOp, traits ...int) FunctionOpt {
trait := 0
for _, t := range traits {
trait = trait | t
}
return func(f *FunctionDecl) (*FunctionDecl, error) {
if f.singleton != nil {
return nil, fmt.Errorf("function already has a singleton binding: %s", f.Name())
}
f.singleton = &functions.Overload{
Operator: f.Name(),
Function: fn,
OperandTrait: trait,
}
return f, nil
}
}
// Overload defines a new global overload with an overload id, argument types, and result type. Through the
// use of OverloadOpt options, the overload may also be configured with a binding, an operand trait, and to
// be non-strict.
//
// Note: function bindings should be commonly configured with Overload instances whereas operand traits and
// strict-ness should be rare occurrences.
func Overload(overloadID string,
args []*types.Type, resultType *types.Type,
opts ...OverloadOpt) FunctionOpt {
return newOverload(overloadID, false, args, resultType, opts...)
}
// MemberOverload defines a new receiver-style overload (or member function) with an overload id, argument types,
// and result type. Through the use of OverloadOpt options, the overload may also be configured with a binding,
// an operand trait, and to be non-strict.
//
// Note: function bindings should be commonly configured with Overload instances whereas operand traits and
// strict-ness should be rare occurrences.
func MemberOverload(overloadID string,
args []*types.Type, resultType *types.Type,
opts ...OverloadOpt) FunctionOpt {
return newOverload(overloadID, true, args, resultType, opts...)
}
func newOverload(overloadID string,
memberFunction bool, args []*types.Type, resultType *types.Type,
opts ...OverloadOpt) FunctionOpt {
return func(f *FunctionDecl) (*FunctionDecl, error) {
overload, err := newOverloadInternal(overloadID, memberFunction, args, resultType, opts...)
if err != nil {
return nil, err
}
err = f.AddOverload(overload)
if err != nil {
return nil, err
}
return f, nil
}
}
func newOverloadInternal(overloadID string,
memberFunction bool, args []*types.Type, resultType *types.Type,
opts ...OverloadOpt) (*OverloadDecl, error) {
overload := &OverloadDecl{
id: overloadID,
argTypes: args,
resultType: resultType,
isMemberFunction: memberFunction,
}
var err error
for _, opt := range opts {
overload, err = opt(overload)
if err != nil {
return nil, err
}
}
return overload, nil
}
// OverloadDecl contains the definition of a single overload id with a specific signature, and an optional
// implementation.
type OverloadDecl struct {
id string
doc string
argTypes []*types.Type
resultType *types.Type
isMemberFunction bool
// hasLateBinding indicates that the function has a binding which is not known at compile time.
// This is useful for functions which have side-effects or are not deterministically computable.
hasLateBinding bool
// nonStrict indicates that the function will accept error and unknown arguments as inputs.
nonStrict bool
// operandTrait indicates whether the member argument should have a specific type-trait.
//
// This is useful for creating overloads which operate on a type-interface rather than a concrete type.
operandTrait int
// Function implementation options. Optional, but encouraged.
// unaryOp is a function binding that takes a single argument.
unaryOp functions.UnaryOp
// binaryOp is a function binding that takes two arguments.
binaryOp functions.BinaryOp
// functionOp is a catch-all for zero-arity and three-plus arity functions.
functionOp functions.FunctionOp
}
// Examples returns a list of string examples for the overload.
func (o *OverloadDecl) Examples() []string {
var emptySet []string
if o == nil || len(o.doc) == 0 {
return emptySet
}
return common.ParseDescriptions(o.doc)
}
// ID mirrors the overload signature and provides a unique id which may be referenced within the type-checker
// and interpreter to optimize performance.
//
// The ID format is usually one of two styles:
// global: <functionName>_<argType>_<argTypeN>
// member: <memberType>_<functionName>_<argType>_<argTypeN>
func (o *OverloadDecl) ID() string {
if o == nil {
return ""
}
return o.id
}
// ArgTypes contains the set of argument types expected by the overload.
//
// For member functions ArgTypes[0] represents the member operand type.
func (o *OverloadDecl) ArgTypes() []*types.Type {
if o == nil {
return emptyArgs
}
return o.argTypes
}
// IsMemberFunction indicates whether the overload is a member function
func (o *OverloadDecl) IsMemberFunction() bool {
if o == nil {
return false
}
return o.isMemberFunction
}
// IsNonStrict returns whether the overload accepts errors and unknown values as arguments.
func (o *OverloadDecl) IsNonStrict() bool {
if o == nil {
return false
}
return o.nonStrict
}
// HasLateBinding returns whether the overload has a binding which is not known at compile time.
func (o *OverloadDecl) HasLateBinding() bool {
if o == nil {
return false
}
return o.hasLateBinding
}
// OperandTrait returns the trait mask of the first operand to the overload call, e.g.
// `traits.Indexer`
func (o *OverloadDecl) OperandTrait() int {
if o == nil {
return 0
}
return o.operandTrait
}
// ResultType indicates the output type from calling the function.
func (o *OverloadDecl) ResultType() *types.Type {
if o == nil {
// *types.Type is nil-safe
return nil
}
return o.resultType
}
// TypeParams returns the type parameter names associated with the overload.
func (o *OverloadDecl) TypeParams() []string {
typeParams := map[string]struct{}{}
collectParamNames(typeParams, o.ResultType())
for _, arg := range o.ArgTypes() {
collectParamNames(typeParams, arg)
}
params := make([]string, 0, len(typeParams))
for param := range typeParams {
params = append(params, param)
}
return params
}
// SignatureEquals determines whether the incoming overload declaration signature is equal to the current signature.
//
// Result type, operand trait, and strict-ness are not considered as part of signature equality.
func (o *OverloadDecl) SignatureEquals(other *OverloadDecl) bool {
if o == other {
return true
}
if o.ID() != other.ID() || o.IsMemberFunction() != other.IsMemberFunction() || len(o.ArgTypes()) != len(other.ArgTypes()) {
return false
}
for i, at := range o.ArgTypes() {
oat := other.ArgTypes()[i]
if !at.IsEquivalentType(oat) {
return false
}
}
return o.ResultType().IsEquivalentType(other.ResultType())
}
// SignatureOverlaps indicates whether two functions have non-equal, but overloapping function signatures.
//
// For example, list(dyn) collides with list(string) since the 'dyn' type can contain a 'string' type.
func (o *OverloadDecl) SignatureOverlaps(other *OverloadDecl) bool {
if o.IsMemberFunction() != other.IsMemberFunction() || len(o.ArgTypes()) != len(other.ArgTypes()) {
return false
}
argsOverlap := true
for i, argType := range o.ArgTypes() {
otherArgType := other.ArgTypes()[i]
argsOverlap = argsOverlap &&
(argType.IsAssignableType(otherArgType) ||
otherArgType.IsAssignableType(argType))
}
return argsOverlap
}
// hasBinding indicates whether the overload already has a definition.
func (o *OverloadDecl) hasBinding() bool {
return o != nil && (o.unaryOp != nil || o.binaryOp != nil || o.functionOp != nil)
}
// guardedUnaryOp creates an invocation guard around the provided unary operator, if one is defined.
func (o *OverloadDecl) guardedUnaryOp(funcName string, disableTypeGuards bool) functions.UnaryOp {
if o.unaryOp == nil {
return nil
}
return func(arg ref.Val) ref.Val {
if !o.matchesRuntimeUnarySignature(disableTypeGuards, arg) {
return MaybeNoSuchOverload(funcName, arg)
}
return o.unaryOp(arg)
}
}
// guardedBinaryOp creates an invocation guard around the provided binary operator, if one is defined.
func (o *OverloadDecl) guardedBinaryOp(funcName string, disableTypeGuards bool) functions.BinaryOp {
if o.binaryOp == nil {
return nil
}
return func(arg1, arg2 ref.Val) ref.Val {
if !o.matchesRuntimeBinarySignature(disableTypeGuards, arg1, arg2) {
return MaybeNoSuchOverload(funcName, arg1, arg2)
}
return o.binaryOp(arg1, arg2)
}
}
// guardedFunctionOp creates an invocation guard around the provided variadic function binding, if one is provided.
func (o *OverloadDecl) guardedFunctionOp(funcName string, disableTypeGuards bool) functions.FunctionOp {
if o.functionOp == nil {
return nil
}
return func(args ...ref.Val) ref.Val {
if !o.matchesRuntimeSignature(disableTypeGuards, args...) {
return MaybeNoSuchOverload(funcName, args...)
}
return o.functionOp(args...)
}
}
// matchesRuntimeUnarySignature indicates whether the argument type is runtime assiganble to the overload's expected argument.
func (o *OverloadDecl) matchesRuntimeUnarySignature(disableTypeGuards bool, arg ref.Val) bool {
return matchRuntimeArgType(o.IsNonStrict(), disableTypeGuards, o.ArgTypes()[0], arg) &&
matchOperandTrait(o.OperandTrait(), arg)
}
// matchesRuntimeBinarySignature indicates whether the argument types are runtime assiganble to the overload's expected arguments.
func (o *OverloadDecl) matchesRuntimeBinarySignature(disableTypeGuards bool, arg1, arg2 ref.Val) bool {
return matchRuntimeArgType(o.IsNonStrict(), disableTypeGuards, o.ArgTypes()[0], arg1) &&
matchRuntimeArgType(o.IsNonStrict(), disableTypeGuards, o.ArgTypes()[1], arg2) &&
matchOperandTrait(o.OperandTrait(), arg1)
}
// matchesRuntimeSignature indicates whether the argument types are runtime assiganble to the overload's expected arguments.
func (o *OverloadDecl) matchesRuntimeSignature(disableTypeGuards bool, args ...ref.Val) bool {
if len(args) != len(o.ArgTypes()) {
return false
}
if len(args) == 0 {
return true
}
for i, arg := range args {
if !matchRuntimeArgType(o.IsNonStrict(), disableTypeGuards, o.ArgTypes()[i], arg) {
return false
}
}
return matchOperandTrait(o.OperandTrait(), args[0])
}
func matchRuntimeArgType(nonStrict, disableTypeGuards bool, argType *types.Type, arg ref.Val) bool {
if nonStrict && (disableTypeGuards || types.IsUnknownOrError(arg)) {
return true
}
if types.IsUnknownOrError(arg) {
return false
}
return disableTypeGuards || argType.IsAssignableRuntimeType(arg)
}
func matchOperandTrait(trait int, arg ref.Val) bool {
return trait == 0 || arg.Type().HasTrait(trait) || types.IsUnknownOrError(arg)
}
// OverloadOpt is a functional option for configuring a function overload.
type OverloadOpt func(*OverloadDecl) (*OverloadDecl, error)
// OverloadExamples configures example expressions for the overload.
func OverloadExamples(examples ...string) OverloadOpt {
return func(o *OverloadDecl) (*OverloadDecl, error) {
o.doc = common.MultilineDescription(examples...)
return o, nil
}
}
// UnaryBinding provides the implementation of a unary overload. The provided function is protected by a runtime
// type-guard which ensures runtime type agreement between the overload signature and runtime argument types.
func UnaryBinding(binding functions.UnaryOp) OverloadOpt {
return func(o *OverloadDecl) (*OverloadDecl, error) {
if o.hasBinding() {
return nil, fmt.Errorf("overload already has a binding: %s", o.ID())
}
if len(o.ArgTypes()) != 1 {
return nil, fmt.Errorf("unary function bound to non-unary overload: %s", o.ID())
}
if o.hasLateBinding {
return nil, fmt.Errorf("overload already has a late binding: %s", o.ID())
}
o.unaryOp = binding
return o, nil
}
}
// BinaryBinding provides the implementation of a binary overload. The provided function is protected by a runtime
// type-guard which ensures runtime type agreement between the overload signature and runtime argument types.
func BinaryBinding(binding functions.BinaryOp) OverloadOpt {
return func(o *OverloadDecl) (*OverloadDecl, error) {
if o.hasBinding() {
return nil, fmt.Errorf("overload already has a binding: %s", o.ID())
}
if len(o.ArgTypes()) != 2 {
return nil, fmt.Errorf("binary function bound to non-binary overload: %s", o.ID())
}
if o.hasLateBinding {
return nil, fmt.Errorf("overload already has a late binding: %s", o.ID())
}
o.binaryOp = binding
return o, nil
}
}
// FunctionBinding provides the implementation of a variadic overload. The provided function is protected by a runtime
// type-guard which ensures runtime type agreement between the overload signature and runtime argument types.
func FunctionBinding(binding functions.FunctionOp) OverloadOpt {
return func(o *OverloadDecl) (*OverloadDecl, error) {
if o.hasBinding() {
return nil, fmt.Errorf("overload already has a binding: %s", o.ID())
}
if o.hasLateBinding {
return nil, fmt.Errorf("overload already has a late binding: %s", o.ID())
}
o.functionOp = binding
return o, nil
}
}
// LateFunctionBinding indicates that the function has a binding which is not known at compile time.
// This is useful for functions which have side-effects or are not deterministically computable.
func LateFunctionBinding() OverloadOpt {
return func(o *OverloadDecl) (*OverloadDecl, error) {
if o.hasBinding() {
return nil, fmt.Errorf("overload already has a binding: %s", o.ID())
}
o.hasLateBinding = true
return o, nil
}
}
// OverloadIsNonStrict enables the function to be called with error and unknown argument values.
//
// Note: do not use this option unless absoluately necessary as it should be an uncommon feature.
func OverloadIsNonStrict() OverloadOpt {
return func(o *OverloadDecl) (*OverloadDecl, error) {
o.nonStrict = true
return o, nil
}
}
// OverloadOperandTrait configures a set of traits which the first argument to the overload must implement in order to be
// successfully invoked.
func OverloadOperandTrait(trait int) OverloadOpt {
return func(o *OverloadDecl) (*OverloadDecl, error) {
o.operandTrait = trait
return o, nil
}
}
// NewConstant creates a new constant declaration.
func NewConstant(name string, t *types.Type, v ref.Val) *VariableDecl {
return &VariableDecl{name: name, varType: t, value: v}
}
// NewVariable creates a new variable declaration.
func NewVariable(name string, t *types.Type) *VariableDecl {
return &VariableDecl{name: name, varType: t}
}
// NewVariableWithDoc creates a new variable declaration with usage documentation.
func NewVariableWithDoc(name string, t *types.Type, doc string) *VariableDecl {
return &VariableDecl{name: name, varType: t, doc: doc}
}
// VariableDecl defines a variable declaration which may optionally have a constant value.
type VariableDecl struct {
name string
doc string
varType *types.Type
value ref.Val
}
// Documentation returns name, type, and description for the variable.
func (v *VariableDecl) Documentation() *common.Doc {
if v == nil {
return nil
}
return common.NewVariableDoc(v.Name(), describeCELType(v.Type()), v.Description())
}
// Name returns the fully-qualified variable name
func (v *VariableDecl) Name() string {
if v == nil {
return ""
}
return v.name
}
// Description returns the usage documentation for the variable, if set.
//
// Good usage instructions provide information about the valid formats, ranges, sizes for the variable type.
func (v *VariableDecl) Description() string {
if v == nil {
return ""
}
return v.doc
}
// Type returns the types.Type value associated with the variable.
func (v *VariableDecl) Type() *types.Type {
if v == nil {
// types.Type is nil-safe
return nil
}
return v.varType
}
// Value returns the constant value associated with the declaration.
func (v *VariableDecl) Value() ref.Val {
if v == nil {
return nil
}
return v.value
}
// DeclarationIsEquivalent returns true if one variable declaration has the same name and same type as the input.
func (v *VariableDecl) DeclarationIsEquivalent(other *VariableDecl) bool {
if v == other {
return true
}
return v.Name() == other.Name() && v.Type().IsEquivalentType(other.Type())
}
// TypeVariable creates a new type identifier for use within a types.Provider
func TypeVariable(t *types.Type) *VariableDecl {
return NewVariable(t.TypeName(), types.NewTypeTypeWithParam(t))
}
// VariableDeclToExprDecl converts a go-native variable declaration into a protobuf-type variable declaration.
func VariableDeclToExprDecl(v *VariableDecl) (*exprpb.Decl, error) {
return variableDeclToExprDecl(v)
}
// variableDeclToExprDecl converts a go-native variable declaration into a protobuf-type variable declaration.
func variableDeclToExprDecl(v *VariableDecl) (*exprpb.Decl, error) {
varType, err := types.TypeToExprType(v.Type())
if err != nil {
return nil, err
}
return chkdecls.NewVarWithDoc(v.Name(), varType, v.doc), nil
}
// FunctionDeclToExprDecl converts a go-native function declaration into a protobuf-typed function declaration.
func FunctionDeclToExprDecl(f *FunctionDecl) (*exprpb.Decl, error) {
return functionDeclToExprDecl(f)
}
// functionDeclToExprDecl converts a go-native function declaration into a protobuf-typed function declaration.
func functionDeclToExprDecl(f *FunctionDecl) (*exprpb.Decl, error) {
overloads := make([]*exprpb.Decl_FunctionDecl_Overload, len(f.overloads))
for i, oID := range f.overloadOrdinals {
o := f.overloads[oID]
paramNames := map[string]struct{}{}
argTypes := make([]*exprpb.Type, len(o.ArgTypes()))
for j, a := range o.ArgTypes() {
collectParamNames(paramNames, a)
at, err := types.TypeToExprType(a)
if err != nil {
return nil, err
}
argTypes[j] = at
}
collectParamNames(paramNames, o.ResultType())
resultType, err := types.TypeToExprType(o.ResultType())
if err != nil {
return nil, err
}
if len(paramNames) == 0 {
if o.IsMemberFunction() {
overloads[i] = chkdecls.NewInstanceOverload(oID, argTypes, resultType)
} else {
overloads[i] = chkdecls.NewOverload(oID, argTypes, resultType)
}
} else {
params := []string{}
for pn := range paramNames {
params = append(params, pn)
}
if o.IsMemberFunction() {
overloads[i] = chkdecls.NewParameterizedInstanceOverload(oID, argTypes, resultType, params)
} else {
overloads[i] = chkdecls.NewParameterizedOverload(oID, argTypes, resultType, params)
}
}
doc := common.MultilineDescription(o.Examples()...)
overloads[i].Doc = doc
}
return chkdecls.NewFunctionWithDoc(f.Name(), f.Description(), overloads...), nil
}
func collectParamNames(paramNames map[string]struct{}, arg *types.Type) {
if arg.Kind() == types.TypeParamKind {
paramNames[arg.TypeName()] = struct{}{}
}
for _, param := range arg.Parameters() {
collectParamNames(paramNames, param)
}
}
func formatSignature(fnName string, o *OverloadDecl) string {
if opName, isOperator := operators.FindReverse(fnName); isOperator {
if opName == "" {
opName = fnName
}
return formatOperator(opName, o)
}
return formatCall(fnName, o)
}
func formatOperator(opName string, o *OverloadDecl) string {
args := o.ArgTypes()
argTypes := make([]string, len(o.ArgTypes()))
for j, a := range args {
argTypes[j] = describeCELType(a)
}
ret := describeCELType(o.ResultType())
switch len(args) {
case 1:
return fmt.Sprintf("%s%s -> %s", opName, argTypes[0], ret)
case 2:
if opName == operators.Index {
return fmt.Sprintf("%s[%s] -> %s", argTypes[0], argTypes[1], ret)
}
return fmt.Sprintf("%s %s %s -> %s", argTypes[0], opName, argTypes[1], ret)
default:
if opName == operators.Conditional {
return fmt.Sprint("bool ? <T> : <T> -> <T>")
}
return formatCall(opName, o)
}
}
func formatCall(funcName string, o *OverloadDecl) string {
args := make([]string, len(o.ArgTypes()))
ret := describeCELType(o.ResultType())
for j, a := range o.ArgTypes() {
args[j] = describeCELType(a)
}
if o.IsMemberFunction() {
target := args[0]
args = args[1:]
return fmt.Sprintf("%s.%s(%s) -> %s", target, funcName, strings.Join(args, ", "), ret)
}
return fmt.Sprintf("%s(%s) -> %s", funcName, strings.Join(args, ", "), ret)
}
func describeCELType(t *types.Type) string {
if t.Kind() == types.TypeKind {
return "type"
}
return t.String()
}
var (
emptyArgs []*types.Type
)
// Copyright 2018 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Package common defines types and utilities common to expression parsing,
// checking, and interpretation
package common
import (
"strings"
"unicode"
)
// DocKind indicates the type of documentation element.
type DocKind int
const (
// DocEnv represents environment variable documentation.
DocEnv DocKind = iota + 1
// DocFunction represents function documentation.
DocFunction
// DocOverload represents function overload documentation.
DocOverload
// DocVariable represents variable documentation.
DocVariable
// DocMacro represents macro documentation.
DocMacro
// DocExample represents example documentation.
DocExample
)
// Doc holds the documentation details for a specific program element like
// a variable, function, macro, or example.
type Doc struct {
// Kind specifies the type of documentation element (e.g., Function, Variable).
Kind DocKind
// Name is the identifier of the documented element (e.g., function name, variable name).
Name string
// Type is the data type associated with the element, primarily used for variables.
Type string
// Signature represents the function or overload signature.
Signature string
// Description holds the textual description of the element, potentially spanning multiple lines.
Description string
// Children holds nested documentation elements, such as overloads for a function
// or examples for a function/macro.
Children []*Doc
}
// MultilineDescription combines multiple lines into a newline separated string.
func MultilineDescription(lines ...string) string {
return strings.Join(lines, "\n")
}
// ParseDescription takes a single string containing newline characters and splits
// it into a multiline description. All empty lines will be skipped.
//
// Returns an empty string if the input string is empty.
func ParseDescription(doc string) string {
var lines []string
if len(doc) != 0 {
// Split the input string by newline characters.
for _, line := range strings.Split(doc, "\n") {
l := strings.TrimRightFunc(line, unicode.IsSpace)
if len(l) == 0 {
continue
}
lines = append(lines, l)
}
}
// Return an empty slice if the input is empty.
return MultilineDescription(lines...)
}
// ParseDescriptions splits a documentation string into multiple multi-line description
// sections, using blank lines as delimiters.
func ParseDescriptions(doc string) []string {
var examples []string
if len(doc) != 0 {
lines := strings.Split(doc, "\n")
lineStart := 0
for i, l := range lines {
// Trim trailing whitespace to identify effectively blank lines.
l = strings.TrimRightFunc(l, unicode.IsSpace)
// If a line is blank, it marks the end of the current section.
if len(l) == 0 {
// Start the next section after the blank line.
ex := lines[lineStart:i]
if len(ex) != 0 {
examples = append(examples, MultilineDescription(ex...))
}
lineStart = i + 1
}
}
// Append the last section if it wasn't terminated by a blank line.
if lineStart < len(lines) {
examples = append(examples, MultilineDescription(lines[lineStart:]...))
}
}
return examples
}
// NewVariableDoc creates a new Doc struct specifically for documenting a variable.
func NewVariableDoc(name, celType, description string) *Doc {
return &Doc{
Kind: DocVariable,
Name: name,
Type: celType,
Description: ParseDescription(description),
}
}
// NewFunctionDoc creates a new Doc struct for documenting a function.
func NewFunctionDoc(name, description string, overloads ...*Doc) *Doc {
return &Doc{
Kind: DocFunction,
Name: name,
Description: ParseDescription(description),
Children: overloads,
}
}
// NewOverloadDoc creates a new Doc struct for a function example.
func NewOverloadDoc(id, signature string, examples ...*Doc) *Doc {
return &Doc{
Kind: DocOverload,
Name: id,
Signature: signature,
Children: examples,
}
}
// NewMacroDoc creates a new Doc struct for documenting a macro.
func NewMacroDoc(name, description string, examples ...*Doc) *Doc {
return &Doc{
Kind: DocMacro,
Name: name,
Description: ParseDescription(description),
Children: examples,
}
}
// NewExampleDoc creates a new Doc struct specifically for holding an example.
func NewExampleDoc(ex string) *Doc {
return &Doc{
Kind: DocExample,
Description: ex,
}
}
// Documentor is an interface for types that can provide their own documentation.
type Documentor interface {
// Documentation returns the documentation coded by the DocKind to assist
// with text formatting.
Documentation() *Doc
}
// Copyright 2025 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Package env provides a representation of a CEL environment.
package env
import (
"errors"
"fmt"
"math"
"strconv"
"strings"
"github.com/google/cel-go/common/decls"
"github.com/google/cel-go/common/types"
)
// NewConfig creates an instance of a YAML serializable CEL environment configuration.
func NewConfig(name string) *Config {
return &Config{
Name: name,
}
}
// Config represents a serializable form of the CEL environment configuration.
//
// Note: custom validations, feature flags, and performance tuning parameters are not (yet)
// considered part of the core CEL environment configuration and should be managed separately
// until a common convention for such settings is developed.
type Config struct {
Name string `yaml:"name,omitempty"`
Description string `yaml:"description,omitempty"`
Container string `yaml:"container,omitempty"`
Imports []*Import `yaml:"imports,omitempty"`
StdLib *LibrarySubset `yaml:"stdlib,omitempty"`
Extensions []*Extension `yaml:"extensions,omitempty"`
ContextVariable *ContextVariable `yaml:"context_variable,omitempty"`
Variables []*Variable `yaml:"variables,omitempty"`
Functions []*Function `yaml:"functions,omitempty"`
Validators []*Validator `yaml:"validators,omitempty"`
Features []*Feature `yaml:"features,omitempty"`
}
// Validate validates the whole configuration is well-formed.
func (c *Config) Validate() error {
if c == nil {
return nil
}
var errs []error
for _, imp := range c.Imports {
if err := imp.Validate(); err != nil {
errs = append(errs, err)
}
}
if err := c.StdLib.Validate(); err != nil {
errs = append(errs, err)
}
for _, ext := range c.Extensions {
if err := ext.Validate(); err != nil {
errs = append(errs, err)
}
}
if err := c.ContextVariable.Validate(); err != nil {
errs = append(errs, err)
}
if c.ContextVariable != nil && len(c.Variables) != 0 {
errs = append(errs, errors.New("invalid config: either context variable or variables may be set, but not both"))
}
for _, v := range c.Variables {
if err := v.Validate(); err != nil {
errs = append(errs, err)
}
}
for _, fn := range c.Functions {
if err := fn.Validate(); err != nil {
errs = append(errs, err)
}
}
for _, feat := range c.Features {
if err := feat.Validate(); err != nil {
errs = append(errs, err)
}
}
for _, val := range c.Validators {
if err := val.Validate(); err != nil {
errs = append(errs, err)
}
}
return errors.Join(errs...)
}
// SetContainer configures the container name for this configuration.
func (c *Config) SetContainer(container string) *Config {
c.Container = container
return c
}
// AddVariableDecls adds one or more variables to the config, converting them to serializable values first.
//
// VariableDecl inputs are expected to be well-formed.
func (c *Config) AddVariableDecls(vars ...*decls.VariableDecl) *Config {
convVars := make([]*Variable, len(vars))
for i, v := range vars {
if v == nil {
continue
}
cv := NewVariable(v.Name(), SerializeTypeDesc(v.Type()))
cv.Description = v.Description()
convVars[i] = cv
}
return c.AddVariables(convVars...)
}
// AddVariables adds one or more vairables to the config.
func (c *Config) AddVariables(vars ...*Variable) *Config {
c.Variables = append(c.Variables, vars...)
return c
}
// SetContextVariable configures the ContextVariable for this configuration.
func (c *Config) SetContextVariable(ctx *ContextVariable) *Config {
c.ContextVariable = ctx
return c
}
// AddFunctionDecls adds one or more functions to the config, converting them to serializable values first.
//
// FunctionDecl inputs are expected to be well-formed.
func (c *Config) AddFunctionDecls(funcs ...*decls.FunctionDecl) *Config {
convFuncs := make([]*Function, len(funcs))
for i, fn := range funcs {
if fn == nil {
continue
}
overloads := make([]*Overload, 0, len(fn.OverloadDecls()))
for _, o := range fn.OverloadDecls() {
overloadID := o.ID()
args := make([]*TypeDesc, 0, len(o.ArgTypes()))
for _, a := range o.ArgTypes() {
args = append(args, SerializeTypeDesc(a))
}
ret := SerializeTypeDesc(o.ResultType())
var overload *Overload
if o.IsMemberFunction() {
overload = NewMemberOverload(overloadID, args[0], args[1:], ret)
} else {
overload = NewOverload(overloadID, args, ret)
}
exampleCount := len(o.Examples())
if exampleCount > 0 {
overload.Examples = o.Examples()
}
overloads = append(overloads, overload)
}
cf := NewFunction(fn.Name(), overloads...)
cf.Description = fn.Description()
convFuncs[i] = cf
}
return c.AddFunctions(convFuncs...)
}
// AddFunctions adds one or more functions to the config.
func (c *Config) AddFunctions(funcs ...*Function) *Config {
c.Functions = append(c.Functions, funcs...)
return c
}
// SetStdLib configures the LibrarySubset for the standard library.
func (c *Config) SetStdLib(subset *LibrarySubset) *Config {
c.StdLib = subset
return c
}
// AddImports appends a set of imports to the config.
func (c *Config) AddImports(imps ...*Import) *Config {
c.Imports = append(c.Imports, imps...)
return c
}
// AddExtensions appends a set of extensions to the config.
func (c *Config) AddExtensions(exts ...*Extension) *Config {
c.Extensions = append(c.Extensions, exts...)
return c
}
// AddValidators appends one or more validators to the config.
func (c *Config) AddValidators(vals ...*Validator) *Config {
c.Validators = append(c.Validators, vals...)
return c
}
// AddFeatures appends one or more features to the config.
func (c *Config) AddFeatures(feats ...*Feature) *Config {
c.Features = append(c.Features, feats...)
return c
}
// NewImport returns a serializable import value from the qualified type name.
func NewImport(name string) *Import {
return &Import{Name: name}
}
// Import represents a type name that will be appreviated by its simple name using
// the cel.Abbrevs() option.
type Import struct {
Name string `yaml:"name"`
}
// Validate validates the import configuration is well-formed.
func (imp *Import) Validate() error {
if imp == nil {
return errors.New("invalid import: nil")
}
if imp.Name == "" {
return errors.New("invalid import: missing type name")
}
return nil
}
// NewVariable returns a serializable variable from a name and type definition
func NewVariable(name string, t *TypeDesc) *Variable {
return NewVariableWithDoc(name, t, "")
}
// NewVariableWithDoc returns a serializable variable from a name, type definition, and doc string.
func NewVariableWithDoc(name string, t *TypeDesc, doc string) *Variable {
return &Variable{Name: name, TypeDesc: t, Description: doc}
}
// Variable represents a typed variable declaration which will be published via the
// cel.VariableDecls() option.
type Variable struct {
Name string `yaml:"name"`
Description string `yaml:"description,omitempty"`
// Type represents the type declaration for the variable.
//
// Deprecated: use the embedded *TypeDesc fields directly.
Type *TypeDesc `yaml:"type,omitempty"`
// TypeDesc is an embedded set of fields allowing for the specification of the Variable type.
*TypeDesc `yaml:",inline"`
}
// Validate validates the variable configuration is well-formed.
func (v *Variable) Validate() error {
if v == nil {
return errors.New("invalid variable: nil")
}
if v.Name == "" {
return errors.New("invalid variable: missing variable name")
}
if err := v.GetType().Validate(); err != nil {
return fmt.Errorf("invalid variable %q: %w", v.Name, err)
}
return nil
}
// GetType returns the variable type description.
//
// Note, if both the embedded TypeDesc and the field Type are non-nil, the embedded TypeDesc will
// take precedence.
func (v *Variable) GetType() *TypeDesc {
if v == nil {
return nil
}
if v.TypeDesc != nil {
return v.TypeDesc
}
if v.Type != nil {
return v.Type
}
return nil
}
// AsCELVariable converts the serializable form of the Variable into a CEL environment declaration.
func (v *Variable) AsCELVariable(tp types.Provider) (*decls.VariableDecl, error) {
if err := v.Validate(); err != nil {
return nil, err
}
t, err := v.GetType().AsCELType(tp)
if err != nil {
return nil, fmt.Errorf("invalid variable %q: %w", v.Name, err)
}
return decls.NewVariableWithDoc(v.Name, t, v.Description), nil
}
// NewContextVariable returns a serializable context variable with a specific type name.
func NewContextVariable(typeName string) *ContextVariable {
return &ContextVariable{TypeName: typeName}
}
// ContextVariable represents a structured message whose fields are to be treated as the top-level
// variable identifiers within CEL expressions.
type ContextVariable struct {
// TypeName represents the fully qualified typename of the context variable.
// Currently, only protobuf types are supported.
TypeName string `yaml:"type_name"`
}
// Validate validates the context-variable configuration is well-formed.
func (ctx *ContextVariable) Validate() error {
if ctx == nil {
return nil
}
if ctx.TypeName == "" {
return errors.New("invalid context variable: missing type name")
}
return nil
}
// NewFunction creates a serializable function and overload set.
func NewFunction(name string, overloads ...*Overload) *Function {
return &Function{Name: name, Overloads: overloads}
}
// NewFunctionWithDoc creates a serializable function and overload set.
func NewFunctionWithDoc(name, doc string, overloads ...*Overload) *Function {
return &Function{Name: name, Description: doc, Overloads: overloads}
}
// Function represents the serializable format of a function and its overloads.
type Function struct {
Name string `yaml:"name"`
Description string `yaml:"description,omitempty"`
Overloads []*Overload `yaml:"overloads,omitempty"`
}
// Validate validates the function configuration is well-formed.
func (fn *Function) Validate() error {
if fn == nil {
return errors.New("invalid function: nil")
}
if fn.Name == "" {
return errors.New("invalid function: missing function name")
}
if len(fn.Overloads) == 0 {
return fmt.Errorf("invalid function %q: missing overloads", fn.Name)
}
var errs []error
for _, o := range fn.Overloads {
if err := o.Validate(); err != nil {
errs = append(errs, fmt.Errorf("invalid function %q: %w", fn.Name, err))
}
}
return errors.Join(errs...)
}
// AsCELFunction converts the serializable form of the Function into CEL environment declaration.
func (fn *Function) AsCELFunction(tp types.Provider) (*decls.FunctionDecl, error) {
if err := fn.Validate(); err != nil {
return nil, err
}
opts := make([]decls.FunctionOpt, 0, len(fn.Overloads)+1)
for _, o := range fn.Overloads {
opt, err := o.AsFunctionOption(tp)
opts = append(opts, opt)
if err != nil {
return nil, fmt.Errorf("invalid function %q: %w", fn.Name, err)
}
}
if len(fn.Description) != 0 {
opts = append(opts, decls.FunctionDocs(fn.Description))
}
return decls.NewFunction(fn.Name, opts...)
}
// NewOverload returns a new serializable representation of a global overload.
func NewOverload(id string, args []*TypeDesc, ret *TypeDesc, examples ...string) *Overload {
return &Overload{ID: id, Args: args, Return: ret, Examples: examples}
}
// NewMemberOverload returns a new serializable representation of a member (receiver) overload.
func NewMemberOverload(id string, target *TypeDesc, args []*TypeDesc, ret *TypeDesc, examples ...string) *Overload {
return &Overload{ID: id, Target: target, Args: args, Return: ret, Examples: examples}
}
// Overload represents the serializable format of a function overload.
type Overload struct {
ID string `yaml:"id"`
Examples []string `yaml:"examples,omitempty"`
Target *TypeDesc `yaml:"target,omitempty"`
Args []*TypeDesc `yaml:"args,omitempty"`
Return *TypeDesc `yaml:"return,omitempty"`
}
// Validate validates the overload configuration is well-formed.
func (od *Overload) Validate() error {
if od == nil {
return errors.New("invalid overload: nil")
}
if od.ID == "" {
return errors.New("invalid overload: missing overload id")
}
var errs []error
if od.Target != nil {
if err := od.Target.Validate(); err != nil {
errs = append(errs, fmt.Errorf("invalid overload %q target: %w", od.ID, err))
}
}
for i, arg := range od.Args {
if err := arg.Validate(); err != nil {
errs = append(errs, fmt.Errorf("invalid overload %q arg[%d]: %w", od.ID, i, err))
}
}
if err := od.Return.Validate(); err != nil {
errs = append(errs, fmt.Errorf("invalid overload %q return: %w", od.ID, err))
}
return errors.Join(errs...)
}
// AsFunctionOption converts the serializable form of the Overload into a function declaration option.
func (od *Overload) AsFunctionOption(tp types.Provider) (decls.FunctionOpt, error) {
if err := od.Validate(); err != nil {
return nil, err
}
args := make([]*types.Type, len(od.Args))
var err error
var errs []error
for i, a := range od.Args {
args[i], err = a.AsCELType(tp)
if err != nil {
errs = append(errs, err)
}
}
result, err := od.Return.AsCELType(tp)
if err != nil {
errs = append(errs, err)
}
if od.Target != nil {
t, err := od.Target.AsCELType(tp)
if err != nil {
return nil, errors.Join(append(errs, err)...)
}
args = append([]*types.Type{t}, args...)
return decls.MemberOverload(od.ID, args, result), nil
}
if len(errs) != 0 {
return nil, errors.Join(errs...)
}
return decls.Overload(od.ID, args, result, decls.OverloadExamples(od.Examples...)), nil
}
// NewExtension creates a serializable Extension from a name and version string.
func NewExtension(name string, version uint32) *Extension {
versionString := "latest"
if version < math.MaxUint32 {
versionString = strconv.FormatUint(uint64(version), 10)
}
return &Extension{
Name: name,
Version: versionString,
}
}
// Extension represents a named and optionally versioned extension library configured in the environment.
type Extension struct {
// Name is either the LibraryName() or some short-hand simple identifier which is understood by the config-handler.
Name string `yaml:"name"`
// Version may either be an unsigned long value or the string 'latest'. If empty, the value is treated as '0'.
Version string `yaml:"version,omitempty"`
}
// Validate validates the extension configuration is well-formed.
func (e *Extension) Validate() error {
_, err := e.VersionNumber()
return err
}
// VersionNumber returns the parsed version string, or an error if the version cannot be parsed.
func (e *Extension) VersionNumber() (uint32, error) {
if e == nil {
return 0, fmt.Errorf("invalid extension: nil")
}
if e.Name == "" {
return 0, fmt.Errorf("invalid extension: missing name")
}
if e.Version == "latest" {
return math.MaxUint32, nil
}
if e.Version == "" {
return 0, nil
}
ver, err := strconv.ParseUint(e.Version, 10, 32)
if err != nil {
return 0, fmt.Errorf("invalid extension %q version: %w", e.Name, err)
}
return uint32(ver), nil
}
// NewLibrarySubset returns an empty library subsetting config which permits all library features.
func NewLibrarySubset() *LibrarySubset {
return &LibrarySubset{}
}
// LibrarySubset indicates a subset of the macros and function supported by a subsettable library.
type LibrarySubset struct {
// Disabled indicates whether the library has been disabled, typically only used for
// default-enabled libraries like stdlib.
Disabled bool `yaml:"disabled,omitempty"`
// DisableMacros disables macros for the given library.
DisableMacros bool `yaml:"disable_macros,omitempty"`
// IncludeMacros specifies a set of macro function names to include in the subset.
IncludeMacros []string `yaml:"include_macros,omitempty"`
// ExcludeMacros specifies a set of macro function names to exclude from the subset.
// Note: if IncludeMacros is non-empty, then ExcludeFunctions is ignored.
ExcludeMacros []string `yaml:"exclude_macros,omitempty"`
// IncludeFunctions specifies a set of functions to include in the subset.
//
// Note: the overloads specified in the subset need only specify their ID.
// Note: if IncludeFunctions is non-empty, then ExcludeFunctions is ignored.
IncludeFunctions []*Function `yaml:"include_functions,omitempty"`
// ExcludeFunctions specifies the set of functions to exclude from the subset.
//
// Note: the overloads specified in the subset need only specify their ID.
ExcludeFunctions []*Function `yaml:"exclude_functions,omitempty"`
}
// Validate validates the library configuration is well-formed.
//
// For example, setting both the IncludeMacros and ExcludeMacros together could be confusing
// and create a broken expectation, likewise for IncludeFunctions and ExcludeFunctions.
func (lib *LibrarySubset) Validate() error {
if lib == nil {
return nil
}
var errs []error
if len(lib.IncludeMacros) != 0 && len(lib.ExcludeMacros) != 0 {
errs = append(errs, errors.New("invalid subset: cannot both include and exclude macros"))
}
if len(lib.IncludeFunctions) != 0 && len(lib.ExcludeFunctions) != 0 {
errs = append(errs, errors.New("invalid subset: cannot both include and exclude functions"))
}
return errors.Join(errs...)
}
// SubsetFunction produces a function declaration which matches the supported subset, or nil
// if the function is not supported by the LibrarySubset.
//
// For IncludeFunctions, if the function does not specify a set of overloads to include, the
// whole function definition is included. If overloads are set, then a new function which
// includes only the specified overloads is produced.
//
// For ExcludeFunctions, if the function does not specify a set of overloads to exclude, the
// whole function definition is excluded. If overloads are set, then a new function which
// includes only the permitted overloads is produced.
func (lib *LibrarySubset) SubsetFunction(fn *decls.FunctionDecl) (*decls.FunctionDecl, bool) {
// When lib is null, it should indicate that all values are included in the subset.
if lib == nil {
return fn, true
}
if lib.Disabled {
return nil, false
}
if len(lib.IncludeFunctions) != 0 {
for _, include := range lib.IncludeFunctions {
if include.Name != fn.Name() {
continue
}
if len(include.Overloads) == 0 {
return fn, true
}
overloadIDs := make([]string, len(include.Overloads))
for i, o := range include.Overloads {
overloadIDs[i] = o.ID
}
return fn.Subset(decls.IncludeOverloads(overloadIDs...)), true
}
return nil, false
}
if len(lib.ExcludeFunctions) != 0 {
for _, exclude := range lib.ExcludeFunctions {
if exclude.Name != fn.Name() {
continue
}
if len(exclude.Overloads) == 0 {
return nil, false
}
overloadIDs := make([]string, len(exclude.Overloads))
for i, o := range exclude.Overloads {
overloadIDs[i] = o.ID
}
return fn.Subset(decls.ExcludeOverloads(overloadIDs...)), true
}
return fn, true
}
return fn, true
}
// SubsetMacro indicates whether the macro function should be included in the library subset.
func (lib *LibrarySubset) SubsetMacro(macroFunction string) bool {
// When lib is null, it should indicate that all values are included in the subset.
if lib == nil {
return true
}
if lib.Disabled || lib.DisableMacros {
return false
}
if len(lib.IncludeMacros) != 0 {
for _, name := range lib.IncludeMacros {
if name == macroFunction {
return true
}
}
return false
}
if len(lib.ExcludeMacros) != 0 {
for _, name := range lib.ExcludeMacros {
if name == macroFunction {
return false
}
}
return true
}
return true
}
// SetDisabled disables or enables the library.
func (lib *LibrarySubset) SetDisabled(value bool) *LibrarySubset {
lib.Disabled = value
return lib
}
// SetDisableMacros disables the macros for the library.
func (lib *LibrarySubset) SetDisableMacros(value bool) *LibrarySubset {
lib.DisableMacros = value
return lib
}
// AddIncludedMacros allow-lists one or more macros by function name.
//
// Note, this option will override any excluded macros.
func (lib *LibrarySubset) AddIncludedMacros(macros ...string) *LibrarySubset {
lib.IncludeMacros = append(lib.IncludeMacros, macros...)
return lib
}
// AddExcludedMacros deny-lists one or more macros by function name.
func (lib *LibrarySubset) AddExcludedMacros(macros ...string) *LibrarySubset {
lib.ExcludeMacros = append(lib.ExcludeMacros, macros...)
return lib
}
// AddIncludedFunctions allow-lists one or more functions from the subset.
//
// Note, this option will override any excluded functions.
func (lib *LibrarySubset) AddIncludedFunctions(funcs ...*Function) *LibrarySubset {
lib.IncludeFunctions = append(lib.IncludeFunctions, funcs...)
return lib
}
// AddExcludedFunctions deny-lists one or more functions from the subset.
func (lib *LibrarySubset) AddExcludedFunctions(funcs ...*Function) *LibrarySubset {
lib.ExcludeFunctions = append(lib.ExcludeFunctions, funcs...)
return lib
}
// NewValidator returns a named Validator instance.
func NewValidator(name string) *Validator {
return &Validator{Name: name}
}
// Validator represents a named validator with an optional map-based configuration object.
//
// Note: the map-keys must directly correspond to the internal representation of the original
// validator, and should only use primitive scalar types as values at this time.
type Validator struct {
Name string `yaml:"name"`
Config map[string]any `yaml:"config,omitempty"`
}
// Validate validates the configuration of the validator object.
func (v *Validator) Validate() error {
if v == nil {
return errors.New("invalid validator: nil")
}
if v.Name == "" {
return errors.New("invalid validator: missing name")
}
return nil
}
// SetConfig sets the set of map key-value pairs associated with this validator's configuration.
func (v *Validator) SetConfig(config map[string]any) *Validator {
v.Config = config
return v
}
// ConfigValue retrieves the value associated with the config key name, if one exists.
func (v *Validator) ConfigValue(name string) (any, bool) {
if v == nil {
return nil, false
}
value, found := v.Config[name]
return value, found
}
// NewFeature creates a new feature flag with a boolean enablement flag.
func NewFeature(name string, enabled bool) *Feature {
return &Feature{Name: name, Enabled: enabled}
}
// Feature represents a named boolean feature flag supported by CEL.
type Feature struct {
Name string `yaml:"name"`
Enabled bool `yaml:"enabled"`
}
// Validate validates whether the feature is well-configured.
func (feat *Feature) Validate() error {
if feat == nil {
return errors.New("invalid feature: nil")
}
if feat.Name == "" {
return errors.New("invalid feature: missing name")
}
return nil
}
// NewTypeDesc describes a simple or complex type with parameters.
func NewTypeDesc(typeName string, params ...*TypeDesc) *TypeDesc {
return &TypeDesc{TypeName: typeName, Params: params}
}
// NewTypeParam describe a type-param type.
func NewTypeParam(paramName string) *TypeDesc {
return &TypeDesc{TypeName: paramName, IsTypeParam: true}
}
// TypeDesc represents the serializable format of a CEL *types.Type value.
type TypeDesc struct {
TypeName string `yaml:"type_name"`
Params []*TypeDesc `yaml:"params,omitempty"`
IsTypeParam bool `yaml:"is_type_param,omitempty"`
}
// String implements the strings.Stringer interface method.
func (td *TypeDesc) String() string {
ps := make([]string, len(td.Params))
for i, p := range td.Params {
ps[i] = p.String()
}
typeName := td.TypeName
if len(ps) != 0 {
typeName = fmt.Sprintf("%s(%s)", typeName, strings.Join(ps, ","))
}
return typeName
}
// Validate validates the type configuration is well-formed.
func (td *TypeDesc) Validate() error {
if td == nil {
return errors.New("invalid type: nil")
}
if td.TypeName == "" {
return errors.New("invalid type: missing type name")
}
if td.IsTypeParam && len(td.Params) != 0 {
return errors.New("invalid type: param type cannot have parameters")
}
switch td.TypeName {
case "list":
if len(td.Params) != 1 {
return fmt.Errorf("invalid type: list expects 1 parameter, got %d", len(td.Params))
}
return td.Params[0].Validate()
case "map":
if len(td.Params) != 2 {
return fmt.Errorf("invalid type: map expects 2 parameters, got %d", len(td.Params))
}
if err := td.Params[0].Validate(); err != nil {
return err
}
if err := td.Params[1].Validate(); err != nil {
return err
}
case "optional_type":
if len(td.Params) != 1 {
return fmt.Errorf("invalid type: optional_type expects 1 parameter, got %d", len(td.Params))
}
return td.Params[0].Validate()
default:
}
return nil
}
// AsCELType converts the serializable object to a *types.Type value.
func (td *TypeDesc) AsCELType(tp types.Provider) (*types.Type, error) {
err := td.Validate()
if err != nil {
return nil, err
}
switch td.TypeName {
case "dyn":
return types.DynType, nil
case "map":
kt, err := td.Params[0].AsCELType(tp)
if err != nil {
return nil, err
}
vt, err := td.Params[1].AsCELType(tp)
if err != nil {
return nil, err
}
return types.NewMapType(kt, vt), nil
case "list":
et, err := td.Params[0].AsCELType(tp)
if err != nil {
return nil, err
}
return types.NewListType(et), nil
case "optional_type":
et, err := td.Params[0].AsCELType(tp)
if err != nil {
return nil, err
}
return types.NewOptionalType(et), nil
default:
if td.IsTypeParam {
return types.NewTypeParamType(td.TypeName), nil
}
if msgType, found := tp.FindStructType(td.TypeName); found {
// First parameter is the type name.
return msgType.Parameters()[0], nil
}
t, found := tp.FindIdent(td.TypeName)
if !found {
return nil, fmt.Errorf("undefined type name: %q", td.TypeName)
}
_, ok := t.(*types.Type)
if ok && len(td.Params) == 0 {
return t.(*types.Type), nil
}
params := make([]*types.Type, len(td.Params))
for i, p := range td.Params {
params[i], err = p.AsCELType(tp)
if err != nil {
return nil, err
}
}
return types.NewOpaqueType(td.TypeName, params...), nil
}
}
// SerializeTypeDesc converts a CEL native *types.Type to a serializable TypeDesc.
func SerializeTypeDesc(t *types.Type) *TypeDesc {
typeName := t.TypeName()
if t.Kind() == types.TypeParamKind {
return NewTypeParam(typeName)
}
if t != types.NullType && t.IsAssignableType(types.NullType) {
if wrapperTypeName, found := wrapperTypes[t.Kind()]; found {
return NewTypeDesc(wrapperTypeName)
}
}
var params []*TypeDesc
for _, p := range t.Parameters() {
params = append(params, SerializeTypeDesc(p))
}
return NewTypeDesc(typeName, params...)
}
var wrapperTypes = map[types.Kind]string{
types.BoolKind: "google.protobuf.BoolValue",
types.BytesKind: "google.protobuf.BytesValue",
types.DoubleKind: "google.protobuf.DoubleValue",
types.IntKind: "google.protobuf.Int64Value",
types.StringKind: "google.protobuf.StringValue",
types.UintKind: "google.protobuf.UInt64Value",
}
// Copyright 2018 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package common
import (
"fmt"
"strings"
"unicode/utf8"
)
// NewError creates an error associated with an expression id with the given message at the given location.
func NewError(id int64, message string, location Location) *Error {
return &Error{Message: message, Location: location, ExprID: id}
}
// Error type which references an expression id, a location within source, and a message.
type Error struct {
Location Location
Message string
ExprID int64
}
const (
dot = "."
ind = "^"
wideDot = "\uff0e"
wideInd = "\uff3e"
// maxSnippetLength is the largest number of characters which can be rendered in an error message snippet.
maxSnippetLength = 16384
)
// ToDisplayString decorates the error message with the source location.
func (e *Error) ToDisplayString(source Source) string {
var result = fmt.Sprintf("ERROR: %s:%d:%d: %s",
source.Description(),
e.Location.Line(),
e.Location.Column()+1, // add one to the 0-based column for display
e.Message)
if snippet, found := source.Snippet(e.Location.Line()); found && len(snippet) <= maxSnippetLength {
snippet := strings.Replace(snippet, "\t", " ", -1)
srcLine := "\n | " + snippet
var bytes = []byte(snippet)
var indLine = "\n | "
for i := 0; i < e.Location.Column() && len(bytes) > 0; i++ {
_, sz := utf8.DecodeRune(bytes)
bytes = bytes[sz:]
if sz > 1 {
indLine += wideDot
} else {
indLine += dot
}
}
if _, sz := utf8.DecodeRune(bytes); sz > 1 {
indLine += wideInd
} else {
indLine += ind
}
result += srcLine + indLine
}
return result
}
// Copyright 2018 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package common
import (
"fmt"
"sort"
"strings"
)
// Errors type which contains a list of errors observed during parsing.
type Errors struct {
errors []*Error
source Source
numErrors int
maxErrorsToReport int
}
// NewErrors creates a new instance of the Errors type.
func NewErrors(source Source) *Errors {
src := source
if src == nil {
src = NewTextSource("")
}
return &Errors{
errors: []*Error{},
source: src,
maxErrorsToReport: 100,
}
}
// ReportError records an error at a source location.
func (e *Errors) ReportError(l Location, format string, args ...any) {
e.ReportErrorAtID(0, l, format, args...)
}
// ReportErrorString records an error at a source location.
func (e *Errors) ReportErrorString(l Location, message string) {
e.ReportErrorAtID(0, l, "%s", message)
}
// ReportErrorAtID records an error at a source location and expression id.
func (e *Errors) ReportErrorAtID(id int64, l Location, format string, args ...any) {
e.numErrors++
if e.numErrors > e.maxErrorsToReport {
return
}
err := &Error{
ExprID: id,
Location: l,
Message: fmt.Sprintf(format, args...),
}
e.errors = append(e.errors, err)
}
// GetErrors returns the list of observed errors.
func (e *Errors) GetErrors() []*Error {
return e.errors[:]
}
// Append creates a new Errors object with the current and input errors.
func (e *Errors) Append(errs []*Error) *Errors {
return &Errors{
errors: append(e.errors[:], errs...),
source: e.source,
numErrors: e.numErrors + len(errs),
maxErrorsToReport: e.maxErrorsToReport,
}
}
// ToDisplayString returns the error set to a newline delimited string.
func (e *Errors) ToDisplayString() string {
errorsInString := e.maxErrorsToReport
if e.numErrors > e.maxErrorsToReport {
// add one more error to indicate the number of errors truncated.
errorsInString++
} else {
// otherwise the error set will just contain the number of errors.
errorsInString = e.numErrors
}
result := make([]string, errorsInString)
sort.SliceStable(e.errors, func(i, j int) bool {
ei := e.errors[i].Location
ej := e.errors[j].Location
return ei.Line() < ej.Line() ||
(ei.Line() == ej.Line() && ei.Column() < ej.Column())
})
for i, err := range e.errors {
// This can happen during the append of two errors objects
if i >= e.maxErrorsToReport {
break
}
result[i] = err.ToDisplayString(e.source)
}
if e.numErrors > e.maxErrorsToReport {
result[e.maxErrorsToReport] = fmt.Sprintf("%d more errors were truncated", e.numErrors-e.maxErrorsToReport)
}
return strings.Join(result, "\n")
}
// Copyright 2018 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package common
// Location interface to represent a location within Source.
type Location interface {
Line() int // 1-based line number within source.
Column() int // 0-based column number within source.
}
// SourceLocation helper type to manually construct a location.
type SourceLocation struct {
line int
column int
}
var (
// Location implements the SourceLocation interface.
_ Location = &SourceLocation{}
// NoLocation is a particular illegal location.
NoLocation = &SourceLocation{-1, -1}
)
// NewLocation creates a new location.
func NewLocation(line, column int) Location {
return &SourceLocation{
line: line,
column: column}
}
// Line returns the 1-based line of the location.
func (l *SourceLocation) Line() int {
return l.line
}
// Column returns the 0-based column number of the location.
func (l *SourceLocation) Column() int {
return l.column
}
// Copyright 2018 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Package operators defines the internal function names of operators.
//
// All operators in the expression language are modelled as function calls.
package operators
// String "names" for CEL operators.
const (
// Symbolic operators.
Conditional = "_?_:_"
LogicalAnd = "_&&_"
LogicalOr = "_||_"
LogicalNot = "!_"
Equals = "_==_"
NotEquals = "_!=_"
Less = "_<_"
LessEquals = "_<=_"
Greater = "_>_"
GreaterEquals = "_>=_"
Add = "_+_"
Subtract = "_-_"
Multiply = "_*_"
Divide = "_/_"
Modulo = "_%_"
Negate = "-_"
Index = "_[_]"
OptIndex = "_[?_]"
OptSelect = "_?._"
// Macros, must have a valid identifier.
Has = "has"
All = "all"
Exists = "exists"
ExistsOne = "exists_one"
Map = "map"
Filter = "filter"
// Named operators, must not have be valid identifiers.
NotStrictlyFalse = "@not_strictly_false"
In = "@in"
// Deprecated: named operators with valid identifiers.
OldNotStrictlyFalse = "__not_strictly_false__"
OldIn = "_in_"
)
var (
operators = map[string]string{
"+": Add,
"/": Divide,
"==": Equals,
">": Greater,
">=": GreaterEquals,
"in": In,
"<": Less,
"<=": LessEquals,
"%": Modulo,
"*": Multiply,
"!=": NotEquals,
"-": Subtract,
}
// operatorMap of the operator symbol which refers to a struct containing the display name,
// if applicable, the operator precedence, and the arity.
//
// If the symbol does not have a display name listed in the map, it is only because it requires
// special casing to render properly as text.
operatorMap = map[string]struct {
displayName string
precedence int
arity int
}{
Conditional: {displayName: "", precedence: 8, arity: 3},
LogicalOr: {displayName: "||", precedence: 7, arity: 2},
LogicalAnd: {displayName: "&&", precedence: 6, arity: 2},
Equals: {displayName: "==", precedence: 5, arity: 2},
Greater: {displayName: ">", precedence: 5, arity: 2},
GreaterEquals: {displayName: ">=", precedence: 5, arity: 2},
In: {displayName: "in", precedence: 5, arity: 2},
Less: {displayName: "<", precedence: 5, arity: 2},
LessEquals: {displayName: "<=", precedence: 5, arity: 2},
NotEquals: {displayName: "!=", precedence: 5, arity: 2},
OldIn: {displayName: "in", precedence: 5, arity: 2},
Add: {displayName: "+", precedence: 4, arity: 2},
Subtract: {displayName: "-", precedence: 4, arity: 2},
Divide: {displayName: "/", precedence: 3, arity: 2},
Modulo: {displayName: "%", precedence: 3, arity: 2},
Multiply: {displayName: "*", precedence: 3, arity: 2},
LogicalNot: {displayName: "!", precedence: 2, arity: 1},
Negate: {displayName: "-", precedence: 2, arity: 1},
Index: {displayName: "", precedence: 1, arity: 2},
OptIndex: {displayName: "", precedence: 1, arity: 2},
OptSelect: {displayName: "", precedence: 1, arity: 2},
}
)
// Find the internal function name for an operator, if the input text is one.
func Find(text string) (string, bool) {
op, found := operators[text]
return op, found
}
// FindReverse returns the unmangled, text representation of the operator.
func FindReverse(symbol string) (string, bool) {
op, found := operatorMap[symbol]
if !found {
return "", false
}
return op.displayName, true
}
// FindReverseBinaryOperator returns the unmangled, text representation of a binary operator.
//
// If the symbol does refer to an operator, but the operator does not have a display name the
// result is false.
func FindReverseBinaryOperator(symbol string) (string, bool) {
op, found := operatorMap[symbol]
if !found || op.arity != 2 {
return "", false
}
if op.displayName == "" {
return "", false
}
return op.displayName, true
}
// Precedence returns the operator precedence, where the higher the number indicates
// higher precedence operations.
func Precedence(symbol string) int {
op, found := operatorMap[symbol]
if !found {
return 0
}
return op.precedence
}
// Arity returns the number of argument the operator takes
// -1 is returned if an undefined symbol is provided
func Arity(symbol string) int {
op, found := operatorMap[symbol]
if !found {
return -1
}
return op.arity
}
// Copyright 2018 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Package overloads defines the internal overload identifiers for function and
// operator overloads.
package overloads
// Boolean logic overloads
const (
Conditional = "conditional"
LogicalAnd = "logical_and"
LogicalOr = "logical_or"
LogicalNot = "logical_not"
NotStrictlyFalse = "not_strictly_false"
Equals = "equals"
NotEquals = "not_equals"
LessBool = "less_bool"
LessInt64 = "less_int64"
LessInt64Double = "less_int64_double"
LessInt64Uint64 = "less_int64_uint64"
LessUint64 = "less_uint64"
LessUint64Double = "less_uint64_double"
LessUint64Int64 = "less_uint64_int64"
LessDouble = "less_double"
LessDoubleInt64 = "less_double_int64"
LessDoubleUint64 = "less_double_uint64"
LessString = "less_string"
LessBytes = "less_bytes"
LessTimestamp = "less_timestamp"
LessDuration = "less_duration"
LessEqualsBool = "less_equals_bool"
LessEqualsInt64 = "less_equals_int64"
LessEqualsInt64Double = "less_equals_int64_double"
LessEqualsInt64Uint64 = "less_equals_int64_uint64"
LessEqualsUint64 = "less_equals_uint64"
LessEqualsUint64Double = "less_equals_uint64_double"
LessEqualsUint64Int64 = "less_equals_uint64_int64"
LessEqualsDouble = "less_equals_double"
LessEqualsDoubleInt64 = "less_equals_double_int64"
LessEqualsDoubleUint64 = "less_equals_double_uint64"
LessEqualsString = "less_equals_string"
LessEqualsBytes = "less_equals_bytes"
LessEqualsTimestamp = "less_equals_timestamp"
LessEqualsDuration = "less_equals_duration"
GreaterBool = "greater_bool"
GreaterInt64 = "greater_int64"
GreaterInt64Double = "greater_int64_double"
GreaterInt64Uint64 = "greater_int64_uint64"
GreaterUint64 = "greater_uint64"
GreaterUint64Double = "greater_uint64_double"
GreaterUint64Int64 = "greater_uint64_int64"
GreaterDouble = "greater_double"
GreaterDoubleInt64 = "greater_double_int64"
GreaterDoubleUint64 = "greater_double_uint64"
GreaterString = "greater_string"
GreaterBytes = "greater_bytes"
GreaterTimestamp = "greater_timestamp"
GreaterDuration = "greater_duration"
GreaterEqualsBool = "greater_equals_bool"
GreaterEqualsInt64 = "greater_equals_int64"
GreaterEqualsInt64Double = "greater_equals_int64_double"
GreaterEqualsInt64Uint64 = "greater_equals_int64_uint64"
GreaterEqualsUint64 = "greater_equals_uint64"
GreaterEqualsUint64Double = "greater_equals_uint64_double"
GreaterEqualsUint64Int64 = "greater_equals_uint64_int64"
GreaterEqualsDouble = "greater_equals_double"
GreaterEqualsDoubleInt64 = "greater_equals_double_int64"
GreaterEqualsDoubleUint64 = "greater_equals_double_uint64"
GreaterEqualsString = "greater_equals_string"
GreaterEqualsBytes = "greater_equals_bytes"
GreaterEqualsTimestamp = "greater_equals_timestamp"
GreaterEqualsDuration = "greater_equals_duration"
)
// Math overloads
const (
AddInt64 = "add_int64"
AddUint64 = "add_uint64"
AddDouble = "add_double"
AddString = "add_string"
AddBytes = "add_bytes"
AddList = "add_list"
AddTimestampDuration = "add_timestamp_duration"
AddDurationTimestamp = "add_duration_timestamp"
AddDurationDuration = "add_duration_duration"
SubtractInt64 = "subtract_int64"
SubtractUint64 = "subtract_uint64"
SubtractDouble = "subtract_double"
SubtractTimestampTimestamp = "subtract_timestamp_timestamp"
SubtractTimestampDuration = "subtract_timestamp_duration"
SubtractDurationDuration = "subtract_duration_duration"
MultiplyInt64 = "multiply_int64"
MultiplyUint64 = "multiply_uint64"
MultiplyDouble = "multiply_double"
DivideInt64 = "divide_int64"
DivideUint64 = "divide_uint64"
DivideDouble = "divide_double"
ModuloInt64 = "modulo_int64"
ModuloUint64 = "modulo_uint64"
NegateInt64 = "negate_int64"
NegateDouble = "negate_double"
)
// Index overloads
const (
IndexList = "index_list"
IndexMap = "index_map"
IndexMessage = "index_message" // TODO: introduce concept of types.Message
)
// In operators
const (
DeprecatedIn = "in"
InList = "in_list"
InMap = "in_map"
InMessage = "in_message" // TODO: introduce concept of types.Message
)
// Size overloads
const (
Size = "size"
SizeString = "size_string"
SizeBytes = "size_bytes"
SizeList = "size_list"
SizeMap = "size_map"
SizeStringInst = "string_size"
SizeBytesInst = "bytes_size"
SizeListInst = "list_size"
SizeMapInst = "map_size"
)
// String function names.
const (
Contains = "contains"
EndsWith = "endsWith"
Matches = "matches"
StartsWith = "startsWith"
)
// Extension function overloads with complex behaviors that need to be referenced in runtime and static analysis cost computations.
const (
ExtQuoteString = "strings_quote"
)
// String function overload names.
const (
ContainsString = "contains_string"
EndsWithString = "ends_with_string"
MatchesString = "matches_string"
StartsWithString = "starts_with_string"
)
// Extension function overloads with complex behaviors that need to be referenced in runtime and static analysis cost computations.
const (
ExtFormatString = "string_format"
)
// Time-based functions.
const (
TimeGetFullYear = "getFullYear"
TimeGetMonth = "getMonth"
TimeGetDayOfYear = "getDayOfYear"
TimeGetDate = "getDate"
TimeGetDayOfMonth = "getDayOfMonth"
TimeGetDayOfWeek = "getDayOfWeek"
TimeGetHours = "getHours"
TimeGetMinutes = "getMinutes"
TimeGetSeconds = "getSeconds"
TimeGetMilliseconds = "getMilliseconds"
)
// Timestamp overloads for time functions without timezones.
const (
TimestampToYear = "timestamp_to_year"
TimestampToMonth = "timestamp_to_month"
TimestampToDayOfYear = "timestamp_to_day_of_year"
TimestampToDayOfMonthZeroBased = "timestamp_to_day_of_month"
TimestampToDayOfMonthOneBased = "timestamp_to_day_of_month_1_based"
TimestampToDayOfWeek = "timestamp_to_day_of_week"
TimestampToHours = "timestamp_to_hours"
TimestampToMinutes = "timestamp_to_minutes"
TimestampToSeconds = "timestamp_to_seconds"
TimestampToMilliseconds = "timestamp_to_milliseconds"
)
// Timestamp overloads for time functions with timezones.
const (
TimestampToYearWithTz = "timestamp_to_year_with_tz"
TimestampToMonthWithTz = "timestamp_to_month_with_tz"
TimestampToDayOfYearWithTz = "timestamp_to_day_of_year_with_tz"
TimestampToDayOfMonthZeroBasedWithTz = "timestamp_to_day_of_month_with_tz"
TimestampToDayOfMonthOneBasedWithTz = "timestamp_to_day_of_month_1_based_with_tz"
TimestampToDayOfWeekWithTz = "timestamp_to_day_of_week_with_tz"
TimestampToHoursWithTz = "timestamp_to_hours_with_tz"
TimestampToMinutesWithTz = "timestamp_to_minutes_with_tz"
TimestampToSecondsWithTz = "timestamp_to_seconds_tz"
TimestampToMillisecondsWithTz = "timestamp_to_milliseconds_with_tz"
)
// Duration overloads for time functions.
const (
DurationToHours = "duration_to_hours"
DurationToMinutes = "duration_to_minutes"
DurationToSeconds = "duration_to_seconds"
DurationToMilliseconds = "duration_to_milliseconds"
)
// Type conversion methods and overloads
const (
TypeConvertInt = "int"
TypeConvertUint = "uint"
TypeConvertDouble = "double"
TypeConvertBool = "bool"
TypeConvertString = "string"
TypeConvertBytes = "bytes"
TypeConvertTimestamp = "timestamp"
TypeConvertDuration = "duration"
TypeConvertType = "type"
TypeConvertDyn = "dyn"
)
// Int conversion functions.
const (
IntToInt = "int64_to_int64"
UintToInt = "uint64_to_int64"
DoubleToInt = "double_to_int64"
StringToInt = "string_to_int64"
TimestampToInt = "timestamp_to_int64"
DurationToInt = "duration_to_int64"
)
// Uint conversion functions.
const (
UintToUint = "uint64_to_uint64"
IntToUint = "int64_to_uint64"
DoubleToUint = "double_to_uint64"
StringToUint = "string_to_uint64"
)
// Double conversion functions.
const (
DoubleToDouble = "double_to_double"
IntToDouble = "int64_to_double"
UintToDouble = "uint64_to_double"
StringToDouble = "string_to_double"
)
// Bool conversion functions.
const (
BoolToBool = "bool_to_bool"
StringToBool = "string_to_bool"
)
// Bytes conversion functions.
const (
BytesToBytes = "bytes_to_bytes"
StringToBytes = "string_to_bytes"
)
// String conversion functions.
const (
StringToString = "string_to_string"
BoolToString = "bool_to_string"
IntToString = "int64_to_string"
UintToString = "uint64_to_string"
DoubleToString = "double_to_string"
BytesToString = "bytes_to_string"
TimestampToString = "timestamp_to_string"
DurationToString = "duration_to_string"
)
// Timestamp conversion functions
const (
TimestampToTimestamp = "timestamp_to_timestamp"
StringToTimestamp = "string_to_timestamp"
IntToTimestamp = "int64_to_timestamp"
)
// Convert duration from string
const (
DurationToDuration = "duration_to_duration"
StringToDuration = "string_to_duration"
IntToDuration = "int64_to_duration"
)
// Convert to dyn
const (
ToDyn = "to_dyn"
)
// Comprehensions helper methods, not directly accessible via a developer.
const (
Iterator = "@iterator"
HasNext = "@hasNext"
Next = "@next"
)
// IsTypeConversionFunction returns whether the input function is a standard library type
// conversion function.
func IsTypeConversionFunction(function string) bool {
switch function {
case TypeConvertBool,
TypeConvertBytes,
TypeConvertDouble,
TypeConvertDuration,
TypeConvertDyn,
TypeConvertInt,
TypeConvertString,
TypeConvertTimestamp,
TypeConvertType,
TypeConvertUint:
return true
default:
return false
}
}
// Copyright 2021 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Package runes provides interfaces and utilities for working with runes.
package runes
import (
"strings"
"unicode/utf8"
)
// Buffer is an interface for accessing a contiguous array of code points.
type Buffer interface {
Get(i int) rune
Slice(i, j int) string
Len() int
}
type emptyBuffer struct{}
func (e *emptyBuffer) Get(i int) rune {
panic("slice index out of bounds")
}
func (e *emptyBuffer) Slice(i, j int) string {
if i != 0 || i != j {
panic("slice index out of bounds")
}
return ""
}
func (e *emptyBuffer) Len() int {
return 0
}
var _ Buffer = &emptyBuffer{}
// asciiBuffer is an implementation for an array of code points that contain code points only from
// the ASCII character set.
type asciiBuffer struct {
arr []byte
}
func (a *asciiBuffer) Get(i int) rune {
return rune(uint32(a.arr[i]))
}
func (a *asciiBuffer) Slice(i, j int) string {
return string(a.arr[i:j])
}
func (a *asciiBuffer) Len() int {
return len(a.arr)
}
var _ Buffer = &asciiBuffer{}
// basicBuffer is an implementation for an array of code points that contain code points from both
// the Latin-1 character set and Basic Multilingual Plane.
type basicBuffer struct {
arr []uint16
}
func (b *basicBuffer) Get(i int) rune {
return rune(uint32(b.arr[i]))
}
func (b *basicBuffer) Slice(i, j int) string {
var str strings.Builder
str.Grow((j - i) * 3) // Worst case encoding size for 0xffff is 3.
for ; i < j; i++ {
str.WriteRune(rune(uint32(b.arr[i])))
}
return str.String()
}
func (b *basicBuffer) Len() int {
return len(b.arr)
}
var _ Buffer = &basicBuffer{}
// supplementalBuffer is an implementation for an array of code points that contain code points from
// the Latin-1 character set, Basic Multilingual Plane, or the Supplemental Multilingual Plane.
type supplementalBuffer struct {
arr []rune
}
func (s *supplementalBuffer) Get(i int) rune {
return rune(uint32(s.arr[i]))
}
func (s *supplementalBuffer) Slice(i, j int) string {
return string(s.arr[i:j])
}
func (s *supplementalBuffer) Len() int {
return len(s.arr)
}
var _ Buffer = &supplementalBuffer{}
var nilBuffer = &emptyBuffer{}
// NewBuffer returns an efficient implementation of Buffer for the given text based on the ranges of
// the encoded code points contained within.
//
// Code points are represented as an array of byte, uint16, or rune. This approach ensures that
// each index represents a code point by itself without needing to use an array of rune. At first
// we assume all code points are less than or equal to '\u007f'. If this holds true, the
// underlying storage is a byte array containing only ASCII characters. If we encountered a code
// point above this range but less than or equal to '\uffff' we allocate a uint16 array, copy the
// elements of previous byte array to the uint16 array, and continue. If this holds true, the
// underlying storage is a uint16 array containing only Unicode characters in the Basic Multilingual
// Plane. If we encounter a code point above '\uffff' we allocate an rune array, copy the previous
// elements of the byte or uint16 array, and continue. The underlying storage is an rune array
// containing any Unicode character.
func NewBuffer(data string) Buffer {
buf, _ := newBuffer(data, false)
return buf
}
// NewBufferAndLineOffsets returns an efficient implementation of Buffer for the given text based on
// the ranges of the encoded code points contained within, as well as returning the line offsets.
//
// Code points are represented as an array of byte, uint16, or rune. This approach ensures that
// each index represents a code point by itself without needing to use an array of rune. At first
// we assume all code points are less than or equal to '\u007f'. If this holds true, the
// underlying storage is a byte array containing only ASCII characters. If we encountered a code
// point above this range but less than or equal to '\uffff' we allocate a uint16 array, copy the
// elements of previous byte array to the uint16 array, and continue. If this holds true, the
// underlying storage is a uint16 array containing only Unicode characters in the Basic Multilingual
// Plane. If we encounter a code point above '\uffff' we allocate an rune array, copy the previous
// elements of the byte or uint16 array, and continue. The underlying storage is an rune array
// containing any Unicode character.
func NewBufferAndLineOffsets(data string) (Buffer, []int32) {
return newBuffer(data, true)
}
func newBuffer(data string, lines bool) (Buffer, []int32) {
if len(data) == 0 {
return nilBuffer, []int32{0}
}
var (
idx = 0
off int32 = 0
buf8 = make([]byte, 0, len(data))
buf16 []uint16
buf32 []rune
offs []int32
)
for idx < len(data) {
r, s := utf8.DecodeRuneInString(data[idx:])
idx += s
if lines && r == '\n' {
offs = append(offs, off+1)
}
if r < utf8.RuneSelf {
buf8 = append(buf8, byte(r))
off++
continue
}
if r <= 0xffff {
buf16 = make([]uint16, len(buf8), len(data))
for i, v := range buf8 {
buf16[i] = uint16(v)
}
buf8 = nil
buf16 = append(buf16, uint16(r))
off++
goto copy16
}
buf32 = make([]rune, len(buf8), len(data))
for i, v := range buf8 {
buf32[i] = rune(uint32(v))
}
buf8 = nil
buf32 = append(buf32, r)
off++
goto copy32
}
if lines {
offs = append(offs, off+1)
}
return &asciiBuffer{
arr: buf8,
}, offs
copy16:
for idx < len(data) {
r, s := utf8.DecodeRuneInString(data[idx:])
idx += s
if lines && r == '\n' {
offs = append(offs, off+1)
}
if r <= 0xffff {
buf16 = append(buf16, uint16(r))
off++
continue
}
buf32 = make([]rune, len(buf16), len(data))
for i, v := range buf16 {
buf32[i] = rune(uint32(v))
}
buf16 = nil
buf32 = append(buf32, r)
off++
goto copy32
}
if lines {
offs = append(offs, off+1)
}
return &basicBuffer{
arr: buf16,
}, offs
copy32:
for idx < len(data) {
r, s := utf8.DecodeRuneInString(data[idx:])
idx += s
if lines && r == '\n' {
offs = append(offs, off+1)
}
buf32 = append(buf32, r)
off++
}
if lines {
offs = append(offs, off+1)
}
return &supplementalBuffer{
arr: buf32,
}, offs
}
// Copyright 2018 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package common
import (
"github.com/google/cel-go/common/runes"
exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1"
)
// Source interface for filter source contents.
type Source interface {
// Content returns the source content represented as a string.
// Examples contents are the single file contents, textbox field,
// or url parameter.
Content() string
// Description gives a brief description of the source.
// Example descriptions are a file name or ui element.
Description() string
// LineOffsets gives the character offsets at which lines occur.
// The zero-th entry should refer to the break between the first
// and second line, or EOF if there is only one line of source.
LineOffsets() []int32
// LocationOffset translates a Location to an offset.
// Given the line and column of the Location returns the
// Location's character offset in the Source, and a bool
// indicating whether the Location was found.
LocationOffset(location Location) (int32, bool)
// OffsetLocation translates a character offset to a Location, or
// false if the conversion was not feasible.
OffsetLocation(offset int32) (Location, bool)
// NewLocation takes an input line and column and produces a Location.
// The default behavior is to treat the line and column as absolute,
// but concrete derivations may use this method to convert a relative
// line and column position into an absolute location.
NewLocation(line, col int) Location
// Snippet returns a line of content and whether the line was found.
Snippet(line int) (string, bool)
}
// The sourceImpl type implementation of the Source interface.
type sourceImpl struct {
runes.Buffer
description string
lineOffsets []int32
}
var _ runes.Buffer = &sourceImpl{}
// TODO(jimlarson) "Character offsets" should index the code points
// within the UTF-8 encoded string. It currently indexes bytes.
// Can be accomplished by using rune[] instead of string for contents.
// NewTextSource creates a new Source from the input text string.
func NewTextSource(text string) Source {
return NewStringSource(text, "<input>")
}
// NewStringSource creates a new Source from the given contents and description.
func NewStringSource(contents string, description string) Source {
// Compute line offsets up front as they are referred to frequently.
buf, offs := runes.NewBufferAndLineOffsets(contents)
return &sourceImpl{
Buffer: buf,
description: description,
lineOffsets: offs,
}
}
// NewInfoSource creates a new Source from a SourceInfo.
func NewInfoSource(info *exprpb.SourceInfo) Source {
return &sourceImpl{
Buffer: runes.NewBuffer(""),
description: info.GetLocation(),
lineOffsets: info.GetLineOffsets(),
}
}
// Content implements the Source interface method.
func (s *sourceImpl) Content() string {
return s.Slice(0, s.Len())
}
// Description implements the Source interface method.
func (s *sourceImpl) Description() string {
return s.description
}
// LineOffsets implements the Source interface method.
func (s *sourceImpl) LineOffsets() []int32 {
return s.lineOffsets
}
// LocationOffset implements the Source interface method.
func (s *sourceImpl) LocationOffset(location Location) (int32, bool) {
if lineOffset, found := s.findLineOffset(location.Line()); found {
return lineOffset + int32(location.Column()), true
}
return -1, false
}
// NewLocation implements the Source interface method.
func (s *sourceImpl) NewLocation(line, col int) Location {
return NewLocation(line, col)
}
// OffsetLocation implements the Source interface method.
func (s *sourceImpl) OffsetLocation(offset int32) (Location, bool) {
line, lineOffset := s.findLine(offset)
return NewLocation(int(line), int(offset-lineOffset)), true
}
// Snippet implements the Source interface method.
func (s *sourceImpl) Snippet(line int) (string, bool) {
charStart, found := s.findLineOffset(line)
if !found || s.Len() == 0 {
return "", false
}
charEnd, found := s.findLineOffset(line + 1)
if found {
return s.Slice(int(charStart), int(charEnd-1)), true
}
return s.Slice(int(charStart), s.Len()), true
}
// findLineOffset returns the offset where the (1-indexed) line begins,
// or false if line doesn't exist.
func (s *sourceImpl) findLineOffset(line int) (int32, bool) {
if line == 1 {
return 0, true
}
if line > 1 && line <= int(len(s.lineOffsets)) {
offset := s.lineOffsets[line-2]
return offset, true
}
return -1, false
}
// findLine finds the line that contains the given character offset and
// returns the line number and offset of the beginning of that line.
// Note that the last line is treated as if it contains all offsets
// beyond the end of the actual source.
func (s *sourceImpl) findLine(characterOffset int32) (int32, int32) {
var line int32 = 1
for _, lineOffset := range s.lineOffsets {
if lineOffset > characterOffset {
break
}
line++
}
if line == 1 {
return line, 0
}
return line, s.lineOffsets[line-2]
}
// Copyright 2018 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Package stdlib contains all of the standard library function declarations and definitions for CEL.
package stdlib
import (
"strconv"
"strings"
"time"
"github.com/google/cel-go/common"
"github.com/google/cel-go/common/decls"
"github.com/google/cel-go/common/functions"
"github.com/google/cel-go/common/operators"
"github.com/google/cel-go/common/overloads"
"github.com/google/cel-go/common/types"
"github.com/google/cel-go/common/types/ref"
"github.com/google/cel-go/common/types/traits"
)
var (
stdFunctions []*decls.FunctionDecl
stdTypes []*decls.VariableDecl
utcTZ = types.String("UTC")
)
func init() {
paramA := types.NewTypeParamType("A")
paramB := types.NewTypeParamType("B")
listOfA := types.NewListType(paramA)
mapOfAB := types.NewMapType(paramA, paramB)
stdTypes = []*decls.VariableDecl{
decls.TypeVariable(types.BoolType),
decls.TypeVariable(types.BytesType),
decls.TypeVariable(types.DoubleType),
decls.TypeVariable(types.DurationType),
decls.TypeVariable(types.IntType),
decls.TypeVariable(listOfA),
decls.TypeVariable(mapOfAB),
decls.TypeVariable(types.NullType),
decls.TypeVariable(types.StringType),
decls.TypeVariable(types.TimestampType),
decls.TypeVariable(types.TypeType),
decls.TypeVariable(types.UintType),
}
stdFunctions = []*decls.FunctionDecl{
// Logical operators. Special-cased within the interpreter.
// Note, the singleton binding prevents extensions from overriding the operator behavior.
function(operators.Conditional,
decls.FunctionDocs(
`The ternary operator tests a boolean predicate and returns the left-hand side `+
`(truthy) expression if true, or the right-hand side (falsy) expression if false`),
decls.Overload(overloads.Conditional, argTypes(types.BoolType, paramA, paramA), paramA,
decls.OverloadIsNonStrict(),
decls.OverloadExamples(
`'hello'.contains('lo') ? 'hi' : 'bye' // 'hi'`,
`32 % 3 == 0 ? 'divisible' : 'not divisible' // 'not divisible'`)),
decls.SingletonFunctionBinding(noFunctionOverrides)),
function(operators.LogicalAnd,
decls.FunctionDocs(
`logically AND two boolean values. Errors and unknown values`,
`are valid inputs and will not halt evaluation.`),
decls.Overload(overloads.LogicalAnd, argTypes(types.BoolType, types.BoolType), types.BoolType,
decls.OverloadIsNonStrict(),
decls.OverloadExamples(
`true && true // true`,
`true && false // false`,
`error && true // error`,
`error && false // false`)),
decls.SingletonBinaryBinding(noBinaryOverrides)),
function(operators.LogicalOr,
decls.FunctionDocs(
`logically OR two boolean values. Errors and unknown values`,
`are valid inputs and will not halt evaluation.`),
decls.Overload(overloads.LogicalOr, argTypes(types.BoolType, types.BoolType), types.BoolType,
decls.OverloadIsNonStrict(),
decls.OverloadExamples(
`true || false // true`,
`false || false // false`,
`error || true // true`,
`error || error // true`)),
decls.SingletonBinaryBinding(noBinaryOverrides)),
function(operators.LogicalNot,
decls.FunctionDocs(`logically negate a boolean value.`),
decls.Overload(overloads.LogicalNot, argTypes(types.BoolType), types.BoolType,
decls.OverloadExamples(
`!true // false`,
`!false // true`,
`!error // error`)),
decls.SingletonUnaryBinding(func(val ref.Val) ref.Val {
b, ok := val.(types.Bool)
if !ok {
return types.MaybeNoSuchOverloadErr(val)
}
return b.Negate()
})),
// Comprehension short-circuiting related function
function(operators.NotStrictlyFalse,
decls.Overload(overloads.NotStrictlyFalse, argTypes(types.BoolType), types.BoolType,
decls.OverloadIsNonStrict(),
decls.UnaryBinding(notStrictlyFalse))),
// Deprecated: __not_strictly_false__
function(operators.OldNotStrictlyFalse,
decls.DisableDeclaration(true), // safe deprecation
decls.Overload(operators.OldNotStrictlyFalse, argTypes(types.BoolType), types.BoolType,
decls.OverloadIsNonStrict(),
decls.UnaryBinding(notStrictlyFalse))),
// Equality / inequality. Special-cased in the interpreter
function(operators.Equals,
decls.FunctionDocs(`compare two values of the same type for equality`),
decls.Overload(overloads.Equals, argTypes(paramA, paramA), types.BoolType,
decls.OverloadExamples(
`1 == 1 // true`,
`'hello' == 'world' // false`,
`bytes('hello') == b'hello' // true`,
`duration('1h') == duration('60m') // true`,
`dyn(3.0) == 3 // true`)),
decls.SingletonBinaryBinding(noBinaryOverrides)),
function(operators.NotEquals,
decls.FunctionDocs(`compare two values of the same type for inequality`),
decls.Overload(overloads.NotEquals, argTypes(paramA, paramA), types.BoolType,
decls.OverloadExamples(
`1 != 2 // true`,
`"a" != "a" // false`,
`3.0 != 3.1 // true`)),
decls.SingletonBinaryBinding(noBinaryOverrides)),
// Mathematical operators
function(operators.Add,
decls.FunctionDocs(
`adds two numeric values or concatenates two strings, bytes,`,
`or lists.`),
decls.Overload(overloads.AddBytes,
argTypes(types.BytesType, types.BytesType), types.BytesType,
decls.OverloadExamples(`b'hi' + bytes('ya') // b'hiya'`)),
decls.Overload(overloads.AddDouble,
argTypes(types.DoubleType, types.DoubleType), types.DoubleType,
decls.OverloadExamples(`3.14 + 1.59 // 4.73`)),
decls.Overload(overloads.AddDurationDuration,
argTypes(types.DurationType, types.DurationType), types.DurationType,
decls.OverloadExamples(`duration('1m') + duration('1s') // duration('1m1s')`)),
decls.Overload(overloads.AddDurationTimestamp,
argTypes(types.DurationType, types.TimestampType), types.TimestampType,
decls.OverloadExamples(`duration('24h') + timestamp('2023-01-01T00:00:00Z') // timestamp('2023-01-02T00:00:00Z')`)),
decls.Overload(overloads.AddTimestampDuration,
argTypes(types.TimestampType, types.DurationType), types.TimestampType,
decls.OverloadExamples(`timestamp('2023-01-01T00:00:00Z') + duration('24h1m2s') // timestamp('2023-01-02T00:01:02Z')`)),
decls.Overload(overloads.AddInt64,
argTypes(types.IntType, types.IntType), types.IntType,
decls.OverloadExamples(`1 + 2 // 3`)),
decls.Overload(overloads.AddList,
argTypes(listOfA, listOfA), listOfA,
decls.OverloadExamples(`[1] + [2, 3] // [1, 2, 3]`)),
decls.Overload(overloads.AddString,
argTypes(types.StringType, types.StringType), types.StringType,
decls.OverloadExamples(`"Hello, " + "world!" // "Hello, world!"`)),
decls.Overload(overloads.AddUint64,
argTypes(types.UintType, types.UintType), types.UintType,
decls.OverloadExamples(`22u + 33u // 55u`)),
decls.SingletonBinaryBinding(func(lhs, rhs ref.Val) ref.Val {
return lhs.(traits.Adder).Add(rhs)
}, traits.AdderType)),
function(operators.Divide,
decls.FunctionDocs(`divide two numbers`),
decls.Overload(overloads.DivideDouble,
argTypes(types.DoubleType, types.DoubleType), types.DoubleType,
decls.OverloadExamples(`7.0 / 2.0 // 3.5`)),
decls.Overload(overloads.DivideInt64,
argTypes(types.IntType, types.IntType), types.IntType,
decls.OverloadExamples(`10 / 2 // 5`)),
decls.Overload(overloads.DivideUint64,
argTypes(types.UintType, types.UintType), types.UintType,
decls.OverloadExamples(`42u / 2u // 21u`)),
decls.SingletonBinaryBinding(func(lhs, rhs ref.Val) ref.Val {
return lhs.(traits.Divider).Divide(rhs)
}, traits.DividerType)),
function(operators.Modulo,
decls.FunctionDocs(`compute the modulus of one integer into another`),
decls.Overload(overloads.ModuloInt64,
argTypes(types.IntType, types.IntType), types.IntType,
decls.OverloadExamples(`3 % 2 // 1`)),
decls.Overload(overloads.ModuloUint64,
argTypes(types.UintType, types.UintType), types.UintType,
decls.OverloadExamples(`6u % 3u // 0u`)),
decls.SingletonBinaryBinding(func(lhs, rhs ref.Val) ref.Val {
return lhs.(traits.Modder).Modulo(rhs)
}, traits.ModderType)),
function(operators.Multiply,
decls.FunctionDocs(`multiply two numbers`),
decls.Overload(overloads.MultiplyDouble,
argTypes(types.DoubleType, types.DoubleType), types.DoubleType,
decls.OverloadExamples(`3.5 * 40.0 // 140.0`)),
decls.Overload(overloads.MultiplyInt64,
argTypes(types.IntType, types.IntType), types.IntType,
decls.OverloadExamples(`-2 * 6 // -12`)),
decls.Overload(overloads.MultiplyUint64,
argTypes(types.UintType, types.UintType), types.UintType,
decls.OverloadExamples(`13u * 3u // 39u`)),
decls.SingletonBinaryBinding(func(lhs, rhs ref.Val) ref.Val {
return lhs.(traits.Multiplier).Multiply(rhs)
}, traits.MultiplierType)),
function(operators.Negate,
decls.FunctionDocs(`negate a numeric value`),
decls.Overload(overloads.NegateDouble, argTypes(types.DoubleType), types.DoubleType,
decls.OverloadExamples(`-(3.14) // -3.14`)),
decls.Overload(overloads.NegateInt64, argTypes(types.IntType), types.IntType,
decls.OverloadExamples(`-(5) // -5`)),
decls.SingletonUnaryBinding(func(val ref.Val) ref.Val {
if types.IsBool(val) {
return types.MaybeNoSuchOverloadErr(val)
}
return val.(traits.Negater).Negate()
}, traits.NegatorType)),
function(operators.Subtract,
decls.FunctionDocs(`subtract two numbers, or two time-related values`),
decls.Overload(overloads.SubtractDouble,
argTypes(types.DoubleType, types.DoubleType), types.DoubleType,
decls.OverloadExamples(`10.5 - 2.0 // 8.5`)),
decls.Overload(overloads.SubtractDurationDuration,
argTypes(types.DurationType, types.DurationType), types.DurationType,
decls.OverloadExamples(`duration('1m') - duration('1s') // duration('59s')`)),
decls.Overload(overloads.SubtractInt64,
argTypes(types.IntType, types.IntType), types.IntType,
decls.OverloadExamples(`5 - 3 // 2`)),
decls.Overload(overloads.SubtractTimestampDuration,
argTypes(types.TimestampType, types.DurationType), types.TimestampType,
decls.OverloadExamples(common.MultilineDescription(
`timestamp('2023-01-10T12:00:00Z')`,
` - duration('12h') // timestamp('2023-01-10T00:00:00Z')`))),
decls.Overload(overloads.SubtractTimestampTimestamp,
argTypes(types.TimestampType, types.TimestampType), types.DurationType,
decls.OverloadExamples(common.MultilineDescription(
`timestamp('2023-01-10T12:00:00Z')`,
` - timestamp('2023-01-10T00:00:00Z') // duration('12h')`))),
decls.Overload(overloads.SubtractUint64,
argTypes(types.UintType, types.UintType), types.UintType,
decls.OverloadExamples(common.MultilineDescription(
`// the subtraction result must be positive, otherwise an overflow`,
`// error is generated.`,
`42u - 3u // 39u`))),
decls.SingletonBinaryBinding(func(lhs, rhs ref.Val) ref.Val {
return lhs.(traits.Subtractor).Subtract(rhs)
}, traits.SubtractorType)),
// Relations operators
function(operators.Less,
decls.FunctionDocs(
`compare two values and return true if the first value is`,
`less than the second`),
decls.Overload(overloads.LessBool,
argTypes(types.BoolType, types.BoolType), types.BoolType,
decls.OverloadExamples(`false < true // true`)),
decls.Overload(overloads.LessInt64,
argTypes(types.IntType, types.IntType), types.BoolType,
decls.OverloadExamples(`-2 < 3 // true`, `1 < 0 // false`)),
decls.Overload(overloads.LessInt64Double,
argTypes(types.IntType, types.DoubleType), types.BoolType,
decls.OverloadExamples(`1 < 1.1 // true`)),
decls.Overload(overloads.LessInt64Uint64,
argTypes(types.IntType, types.UintType), types.BoolType,
decls.OverloadExamples(`1 < 2u // true`)),
decls.Overload(overloads.LessUint64,
argTypes(types.UintType, types.UintType), types.BoolType,
decls.OverloadExamples(`1u < 2u // true`)),
decls.Overload(overloads.LessUint64Double,
argTypes(types.UintType, types.DoubleType), types.BoolType,
decls.OverloadExamples(`1u < 0.9 // false`)),
decls.Overload(overloads.LessUint64Int64,
argTypes(types.UintType, types.IntType), types.BoolType,
decls.OverloadExamples(`1u < 23 // true`, `1u < -1 // false`)),
decls.Overload(overloads.LessDouble,
argTypes(types.DoubleType, types.DoubleType), types.BoolType,
decls.OverloadExamples(`2.0 < 2.4 // true`)),
decls.Overload(overloads.LessDoubleInt64,
argTypes(types.DoubleType, types.IntType), types.BoolType,
decls.OverloadExamples(`2.1 < 3 // true`)),
decls.Overload(overloads.LessDoubleUint64,
argTypes(types.DoubleType, types.UintType), types.BoolType,
decls.OverloadExamples(`2.3 < 2u // false`, `-1.0 < 1u // true`)),
decls.Overload(overloads.LessString,
argTypes(types.StringType, types.StringType), types.BoolType,
decls.OverloadExamples(`'a' < 'b' // true`, `'cat' < 'cab' // false`)),
decls.Overload(overloads.LessBytes,
argTypes(types.BytesType, types.BytesType), types.BoolType,
decls.OverloadExamples(`b'hello' < b'world' // true`)),
decls.Overload(overloads.LessTimestamp,
argTypes(types.TimestampType, types.TimestampType), types.BoolType,
decls.OverloadExamples(`timestamp('2001-01-01T02:03:04Z') < timestamp('2002-02-02T02:03:04Z') // true`)),
decls.Overload(overloads.LessDuration,
argTypes(types.DurationType, types.DurationType), types.BoolType,
decls.OverloadExamples(`duration('1ms') < duration('1s') // true`)),
decls.SingletonBinaryBinding(func(lhs, rhs ref.Val) ref.Val {
cmp := lhs.(traits.Comparer).Compare(rhs)
if cmp == types.IntNegOne {
return types.True
}
if cmp == types.IntOne || cmp == types.IntZero {
return types.False
}
return cmp
}, traits.ComparerType)),
function(operators.LessEquals,
decls.FunctionDocs(
`compare two values and return true if the first value is`,
`less than or equal to the second`),
decls.Overload(overloads.LessEqualsBool,
argTypes(types.BoolType, types.BoolType), types.BoolType,
decls.OverloadExamples(`false <= true // true`)),
decls.Overload(overloads.LessEqualsInt64,
argTypes(types.IntType, types.IntType), types.BoolType,
decls.OverloadExamples(`-2 <= 3 // true`)),
decls.Overload(overloads.LessEqualsInt64Double,
argTypes(types.IntType, types.DoubleType), types.BoolType,
decls.OverloadExamples(`1 <= 1.1 // true`)),
decls.Overload(overloads.LessEqualsInt64Uint64,
argTypes(types.IntType, types.UintType), types.BoolType,
decls.OverloadExamples(`1 <= 2u // true`, `-1 <= 0u // true`)),
decls.Overload(overloads.LessEqualsUint64,
argTypes(types.UintType, types.UintType), types.BoolType,
decls.OverloadExamples(`1u <= 2u // true`)),
decls.Overload(overloads.LessEqualsUint64Double,
argTypes(types.UintType, types.DoubleType), types.BoolType,
decls.OverloadExamples(`1u <= 1.0 // true`, `1u <= 1.1 // true`)),
decls.Overload(overloads.LessEqualsUint64Int64,
argTypes(types.UintType, types.IntType), types.BoolType,
decls.OverloadExamples(`1u <= 23 // true`)),
decls.Overload(overloads.LessEqualsDouble,
argTypes(types.DoubleType, types.DoubleType), types.BoolType,
decls.OverloadExamples(`2.0 <= 2.4 // true`)),
decls.Overload(overloads.LessEqualsDoubleInt64,
argTypes(types.DoubleType, types.IntType), types.BoolType,
decls.OverloadExamples(`2.1 <= 3 // true`)),
decls.Overload(overloads.LessEqualsDoubleUint64,
argTypes(types.DoubleType, types.UintType), types.BoolType,
decls.OverloadExamples(`2.0 <= 2u // true`, `-1.0 <= 1u // true`)),
decls.Overload(overloads.LessEqualsString,
argTypes(types.StringType, types.StringType), types.BoolType,
decls.OverloadExamples(`'a' <= 'b' // true`, `'a' <= 'a' // true`, `'cat' <= 'cab' // false`)),
decls.Overload(overloads.LessEqualsBytes,
argTypes(types.BytesType, types.BytesType), types.BoolType,
decls.OverloadExamples(`b'hello' <= b'world' // true`)),
decls.Overload(overloads.LessEqualsTimestamp,
argTypes(types.TimestampType, types.TimestampType), types.BoolType,
decls.OverloadExamples(`timestamp('2001-01-01T02:03:04Z') <= timestamp('2002-02-02T02:03:04Z') // true`)),
decls.Overload(overloads.LessEqualsDuration,
argTypes(types.DurationType, types.DurationType), types.BoolType,
decls.OverloadExamples(`duration('1ms') <= duration('1s') // true`)),
decls.SingletonBinaryBinding(func(lhs, rhs ref.Val) ref.Val {
cmp := lhs.(traits.Comparer).Compare(rhs)
if cmp == types.IntNegOne || cmp == types.IntZero {
return types.True
}
if cmp == types.IntOne {
return types.False
}
return cmp
}, traits.ComparerType)),
function(operators.Greater,
decls.FunctionDocs(
`compare two values and return true if the first value is`,
`greater than the second`),
decls.Overload(overloads.GreaterBool,
argTypes(types.BoolType, types.BoolType), types.BoolType,
decls.OverloadExamples(`true > false // true`)),
decls.Overload(overloads.GreaterInt64,
argTypes(types.IntType, types.IntType), types.BoolType,
decls.OverloadExamples(`3 > -2 // true`)),
decls.Overload(overloads.GreaterInt64Double,
argTypes(types.IntType, types.DoubleType), types.BoolType,
decls.OverloadExamples(`2 > 1.1 // true`)),
decls.Overload(overloads.GreaterInt64Uint64,
argTypes(types.IntType, types.UintType), types.BoolType,
decls.OverloadExamples(`3 > 2u // true`)),
decls.Overload(overloads.GreaterUint64,
argTypes(types.UintType, types.UintType), types.BoolType,
decls.OverloadExamples(`2u > 1u // true`)),
decls.Overload(overloads.GreaterUint64Double,
argTypes(types.UintType, types.DoubleType), types.BoolType,
decls.OverloadExamples(`2u > 1.9 // true`)),
decls.Overload(overloads.GreaterUint64Int64,
argTypes(types.UintType, types.IntType), types.BoolType,
decls.OverloadExamples(`23u > 1 // true`, `0u > -1 // true`)),
decls.Overload(overloads.GreaterDouble,
argTypes(types.DoubleType, types.DoubleType), types.BoolType,
decls.OverloadExamples(`2.4 > 2.0 // true`)),
decls.Overload(overloads.GreaterDoubleInt64,
argTypes(types.DoubleType, types.IntType), types.BoolType,
decls.OverloadExamples(`3.1 > 3 // true`, `3.0 > 3 // false`)),
decls.Overload(overloads.GreaterDoubleUint64,
argTypes(types.DoubleType, types.UintType), types.BoolType,
decls.OverloadExamples(`2.3 > 2u // true`)),
decls.Overload(overloads.GreaterString,
argTypes(types.StringType, types.StringType), types.BoolType,
decls.OverloadExamples(`'b' > 'a' // true`)),
decls.Overload(overloads.GreaterBytes,
argTypes(types.BytesType, types.BytesType), types.BoolType,
decls.OverloadExamples(`b'world' > b'hello' // true`)),
decls.Overload(overloads.GreaterTimestamp,
argTypes(types.TimestampType, types.TimestampType), types.BoolType,
decls.OverloadExamples(`timestamp('2002-02-02T02:03:04Z') > timestamp('2001-01-01T02:03:04Z') // true`)),
decls.Overload(overloads.GreaterDuration,
argTypes(types.DurationType, types.DurationType), types.BoolType,
decls.OverloadExamples(`duration('1ms') > duration('1us') // true`)),
decls.SingletonBinaryBinding(func(lhs, rhs ref.Val) ref.Val {
cmp := lhs.(traits.Comparer).Compare(rhs)
if cmp == types.IntOne {
return types.True
}
if cmp == types.IntNegOne || cmp == types.IntZero {
return types.False
}
return cmp
}, traits.ComparerType)),
function(operators.GreaterEquals,
decls.FunctionDocs(
`compare two values and return true if the first value is`,
`greater than or equal to the second`),
decls.Overload(overloads.GreaterEqualsBool,
argTypes(types.BoolType, types.BoolType), types.BoolType,
decls.OverloadExamples(`true >= false // true`)),
decls.Overload(overloads.GreaterEqualsInt64,
argTypes(types.IntType, types.IntType), types.BoolType,
decls.OverloadExamples(`3 >= -2 // true`)),
decls.Overload(overloads.GreaterEqualsInt64Double,
argTypes(types.IntType, types.DoubleType), types.BoolType,
decls.OverloadExamples(`2 >= 1.1 // true`, `1 >= 1.0 // true`)),
decls.Overload(overloads.GreaterEqualsInt64Uint64,
argTypes(types.IntType, types.UintType), types.BoolType,
decls.OverloadExamples(`3 >= 2u // true`)),
decls.Overload(overloads.GreaterEqualsUint64,
argTypes(types.UintType, types.UintType), types.BoolType,
decls.OverloadExamples(`2u >= 1u // true`)),
decls.Overload(overloads.GreaterEqualsUint64Double,
argTypes(types.UintType, types.DoubleType), types.BoolType,
decls.OverloadExamples(`2u >= 1.9 // true`)),
decls.Overload(overloads.GreaterEqualsUint64Int64,
argTypes(types.UintType, types.IntType), types.BoolType,
decls.OverloadExamples(`23u >= 1 // true`, `1u >= 1 // true`)),
decls.Overload(overloads.GreaterEqualsDouble,
argTypes(types.DoubleType, types.DoubleType), types.BoolType,
decls.OverloadExamples(`2.4 >= 2.0 // true`)),
decls.Overload(overloads.GreaterEqualsDoubleInt64,
argTypes(types.DoubleType, types.IntType), types.BoolType,
decls.OverloadExamples(`3.1 >= 3 // true`)),
decls.Overload(overloads.GreaterEqualsDoubleUint64,
argTypes(types.DoubleType, types.UintType), types.BoolType,
decls.OverloadExamples(`2.3 >= 2u // true`)),
decls.Overload(overloads.GreaterEqualsString,
argTypes(types.StringType, types.StringType), types.BoolType,
decls.OverloadExamples(`'b' >= 'a' // true`)),
decls.Overload(overloads.GreaterEqualsBytes,
argTypes(types.BytesType, types.BytesType), types.BoolType,
decls.OverloadExamples(`b'world' >= b'hello' // true`)),
decls.Overload(overloads.GreaterEqualsTimestamp,
argTypes(types.TimestampType, types.TimestampType), types.BoolType,
decls.OverloadExamples(`timestamp('2001-01-01T02:03:04Z') >= timestamp('2001-01-01T02:03:04Z') // true`)),
decls.Overload(overloads.GreaterEqualsDuration,
argTypes(types.DurationType, types.DurationType), types.BoolType,
decls.OverloadExamples(`duration('60s') >= duration('1m') // true`)),
decls.SingletonBinaryBinding(func(lhs, rhs ref.Val) ref.Val {
cmp := lhs.(traits.Comparer).Compare(rhs)
if cmp == types.IntOne || cmp == types.IntZero {
return types.True
}
if cmp == types.IntNegOne {
return types.False
}
return cmp
}, traits.ComparerType)),
// Indexing
function(operators.Index,
decls.FunctionDocs(`select a value from a list by index, or value from a map by key`),
decls.Overload(overloads.IndexList, argTypes(listOfA, types.IntType), paramA,
decls.OverloadExamples(`[1, 2, 3][1] // 2`)),
decls.Overload(overloads.IndexMap, argTypes(mapOfAB, paramA), paramB,
decls.OverloadExamples(
`{'key': 'value'}['key'] // 'value'`,
`{'key': 'value'}['missing'] // error`)),
decls.SingletonBinaryBinding(func(lhs, rhs ref.Val) ref.Val {
return lhs.(traits.Indexer).Get(rhs)
}, traits.IndexerType)),
// Collections operators
function(operators.In,
decls.FunctionDocs(`test whether a value exists in a list, or a key exists in a map`),
decls.Overload(overloads.InList, argTypes(paramA, listOfA), types.BoolType,
decls.OverloadExamples(
`2 in [1, 2, 3] // true`,
`"a" in ["b", "c"] // false`)),
decls.Overload(overloads.InMap, argTypes(paramA, mapOfAB), types.BoolType,
decls.OverloadExamples(
`'key1' in {'key1': 'value1', 'key2': 'value2'} // true`,
`3 in {1: "one", 2: "two"} // false`)),
decls.SingletonBinaryBinding(inAggregate)),
function(operators.OldIn,
decls.DisableDeclaration(true), // safe deprecation
decls.Overload(overloads.InList, argTypes(paramA, listOfA), types.BoolType),
decls.Overload(overloads.InMap, argTypes(paramA, mapOfAB), types.BoolType),
decls.SingletonBinaryBinding(inAggregate)),
function(overloads.DeprecatedIn,
decls.DisableDeclaration(true), // safe deprecation
decls.Overload(overloads.InList, argTypes(paramA, listOfA), types.BoolType),
decls.Overload(overloads.InMap, argTypes(paramA, mapOfAB), types.BoolType),
decls.SingletonBinaryBinding(inAggregate)),
function(overloads.Size,
decls.FunctionDocs(
`compute the size of a list or map, the number of characters in a string,`,
`or the number of bytes in a sequence`),
decls.Overload(overloads.SizeBytes, argTypes(types.BytesType), types.IntType,
decls.OverloadExamples(`size(b'123') // 3`)),
decls.MemberOverload(overloads.SizeBytesInst, argTypes(types.BytesType), types.IntType,
decls.OverloadExamples(`b'123'.size() // 3`)),
decls.Overload(overloads.SizeList, argTypes(listOfA), types.IntType,
decls.OverloadExamples(`size([1, 2, 3]) // 3`)),
decls.MemberOverload(overloads.SizeListInst, argTypes(listOfA), types.IntType,
decls.OverloadExamples(`[1, 2, 3].size() // 3`)),
decls.Overload(overloads.SizeMap, argTypes(mapOfAB), types.IntType,
decls.OverloadExamples(`size({'a': 1, 'b': 2}) // 2`)),
decls.MemberOverload(overloads.SizeMapInst, argTypes(mapOfAB), types.IntType,
decls.OverloadExamples(`{'a': 1, 'b': 2}.size() // 2`)),
decls.Overload(overloads.SizeString, argTypes(types.StringType), types.IntType,
decls.OverloadExamples(`size('hello') // 5`)),
decls.MemberOverload(overloads.SizeStringInst, argTypes(types.StringType), types.IntType,
decls.OverloadExamples(`'hello'.size() // 5`)),
decls.SingletonUnaryBinding(func(val ref.Val) ref.Val {
return val.(traits.Sizer).Size()
}, traits.SizerType)),
// Type conversions
function(overloads.TypeConvertType,
decls.FunctionDocs(`convert a value to its type identifier`),
decls.Overload(overloads.TypeConvertType, argTypes(paramA), types.NewTypeTypeWithParam(paramA),
decls.OverloadExamples(
`type(1) // int`,
`type('hello') // string`,
`type(int) // type`,
`type(type) // type`)),
decls.SingletonUnaryBinding(convertToType(types.TypeType))),
// Bool conversions
function(overloads.TypeConvertBool,
decls.FunctionDocs(`convert a value to a boolean`),
decls.Overload(overloads.BoolToBool, argTypes(types.BoolType), types.BoolType,
decls.OverloadExamples(`bool(true) // true`),
decls.UnaryBinding(identity)),
decls.Overload(overloads.StringToBool, argTypes(types.StringType), types.BoolType,
decls.OverloadExamples(`bool('true') // true`, `bool('false') // false`),
decls.UnaryBinding(convertToType(types.BoolType)))),
// Bytes conversions
function(overloads.TypeConvertBytes,
decls.FunctionDocs(`convert a value to bytes`),
decls.Overload(overloads.BytesToBytes, argTypes(types.BytesType), types.BytesType,
decls.OverloadExamples(`bytes(b'abc') // b'abc'`),
decls.UnaryBinding(identity)),
decls.Overload(overloads.StringToBytes, argTypes(types.StringType), types.BytesType,
decls.OverloadExamples(`bytes('hello') // b'hello'`),
decls.UnaryBinding(convertToType(types.BytesType)))),
// Double conversions
function(overloads.TypeConvertDouble,
decls.FunctionDocs(`convert a value to a double`),
decls.Overload(overloads.DoubleToDouble, argTypes(types.DoubleType), types.DoubleType,
decls.OverloadExamples(`double(1.23) // 1.23`),
decls.UnaryBinding(identity)),
decls.Overload(overloads.IntToDouble, argTypes(types.IntType), types.DoubleType,
decls.OverloadExamples(`double(123) // 123.0`),
decls.UnaryBinding(convertToType(types.DoubleType))),
decls.Overload(overloads.StringToDouble, argTypes(types.StringType), types.DoubleType,
decls.OverloadExamples(`double('1.23') // 1.23`),
decls.UnaryBinding(convertToType(types.DoubleType))),
decls.Overload(overloads.UintToDouble, argTypes(types.UintType), types.DoubleType,
decls.OverloadExamples(`double(123u) // 123.0`),
decls.UnaryBinding(convertToType(types.DoubleType)))),
// Duration conversions
function(overloads.TypeConvertDuration,
decls.FunctionDocs(`convert a value to a google.protobuf.Duration`),
decls.Overload(overloads.DurationToDuration, argTypes(types.DurationType), types.DurationType,
decls.OverloadExamples(`duration(duration('1s')) // duration('1s')`),
decls.UnaryBinding(identity)),
decls.Overload(overloads.IntToDuration, argTypes(types.IntType), types.DurationType,
decls.UnaryBinding(convertToType(types.DurationType))),
decls.Overload(overloads.StringToDuration, argTypes(types.StringType), types.DurationType,
decls.OverloadExamples(`duration('1h2m3s') // duration('3723s')`),
decls.UnaryBinding(convertToType(types.DurationType)))),
// Dyn conversions
function(overloads.TypeConvertDyn,
decls.FunctionDocs(`indicate that the type is dynamic for type-checking purposes`),
decls.Overload(overloads.ToDyn, argTypes(paramA), types.DynType,
decls.OverloadExamples(`dyn(1) // 1`)),
decls.SingletonUnaryBinding(identity)),
// Int conversions
function(overloads.TypeConvertInt,
decls.FunctionDocs(`convert a value to an int`),
decls.Overload(overloads.IntToInt, argTypes(types.IntType), types.IntType,
decls.OverloadExamples(`int(123) // 123`),
decls.UnaryBinding(identity)),
decls.Overload(overloads.DoubleToInt, argTypes(types.DoubleType), types.IntType,
decls.OverloadExamples(`int(123.45) // 123`),
decls.UnaryBinding(convertToType(types.IntType))),
decls.Overload(overloads.DurationToInt, argTypes(types.DurationType), types.IntType,
decls.OverloadExamples(`int(duration('1s')) // 1000000000`),
decls.UnaryBinding(convertToType(types.IntType))), // Duration to nanoseconds
decls.Overload(overloads.StringToInt, argTypes(types.StringType), types.IntType,
decls.OverloadExamples(`int('123') // 123`, `int('-456') // -456`),
decls.UnaryBinding(convertToType(types.IntType))),
decls.Overload(overloads.TimestampToInt, argTypes(types.TimestampType), types.IntType,
decls.OverloadExamples(`int(timestamp('1970-01-01T00:00:01Z')) // 1`),
decls.UnaryBinding(convertToType(types.IntType))), // Timestamp to epoch seconds
decls.Overload(overloads.UintToInt, argTypes(types.UintType), types.IntType,
decls.OverloadExamples(`int(123u) // 123`),
decls.UnaryBinding(convertToType(types.IntType)))),
// String conversions
function(overloads.TypeConvertString,
decls.FunctionDocs(`convert a value to a string`),
decls.Overload(overloads.StringToString, argTypes(types.StringType), types.StringType,
decls.OverloadExamples(`string('hello') // 'hello'`),
decls.UnaryBinding(identity)),
decls.Overload(overloads.BoolToString, argTypes(types.BoolType), types.StringType,
decls.OverloadExamples(`string(true) // 'true'`),
decls.UnaryBinding(convertToType(types.StringType))),
decls.Overload(overloads.BytesToString, argTypes(types.BytesType), types.StringType,
decls.OverloadExamples(`string(b'hello') // 'hello'`),
decls.UnaryBinding(convertToType(types.StringType))),
decls.Overload(overloads.DoubleToString, argTypes(types.DoubleType), types.StringType,
decls.UnaryBinding(convertToType(types.StringType)),
decls.OverloadExamples(`string(-1.23e4) // '-12300'`)),
decls.Overload(overloads.DurationToString, argTypes(types.DurationType), types.StringType,
decls.OverloadExamples(`string(duration('1h30m')) // '5400s'`),
decls.UnaryBinding(convertToType(types.StringType))),
decls.Overload(overloads.IntToString, argTypes(types.IntType), types.StringType,
decls.OverloadExamples(`string(-123) // '-123'`),
decls.UnaryBinding(convertToType(types.StringType))),
decls.Overload(overloads.TimestampToString, argTypes(types.TimestampType), types.StringType,
decls.OverloadExamples(`string(timestamp('1970-01-01T00:00:00Z')) // '1970-01-01T00:00:00Z'`),
decls.UnaryBinding(convertToType(types.StringType))),
decls.Overload(overloads.UintToString, argTypes(types.UintType), types.StringType,
decls.OverloadExamples(`string(123u) // '123'`),
decls.UnaryBinding(convertToType(types.StringType)))),
// Timestamp conversions
function(overloads.TypeConvertTimestamp,
decls.FunctionDocs(`convert a value to a google.protobuf.Timestamp`),
decls.Overload(overloads.TimestampToTimestamp, argTypes(types.TimestampType), types.TimestampType,
decls.OverloadExamples(`timestamp(timestamp('2023-01-01T00:00:00Z')) // timestamp('2023-01-01T00:00:00Z')`),
decls.UnaryBinding(identity)),
decls.Overload(overloads.IntToTimestamp, argTypes(types.IntType), types.TimestampType,
decls.OverloadExamples(`timestamp(1) // timestamp('1970-01-01T00:00:01Z')`), // Epoch seconds to Timestamp
decls.UnaryBinding(convertToType(types.TimestampType))),
decls.Overload(overloads.StringToTimestamp, argTypes(types.StringType), types.TimestampType,
decls.OverloadExamples(`timestamp('2025-01-01T12:34:56Z') // timestamp('2025-01-01T12:34:56Z')`),
decls.UnaryBinding(convertToType(types.TimestampType)))),
// Uint conversions
function(overloads.TypeConvertUint,
decls.FunctionDocs(`convert a value to a uint`),
decls.Overload(overloads.UintToUint, argTypes(types.UintType), types.UintType,
decls.OverloadExamples(`uint(123u) // 123u`),
decls.UnaryBinding(identity)),
decls.Overload(overloads.DoubleToUint, argTypes(types.DoubleType), types.UintType,
decls.OverloadExamples(`uint(123.45) // 123u`),
decls.UnaryBinding(convertToType(types.UintType))),
decls.Overload(overloads.IntToUint, argTypes(types.IntType), types.UintType,
decls.OverloadExamples(`uint(123) // 123u`),
decls.UnaryBinding(convertToType(types.UintType))),
decls.Overload(overloads.StringToUint, argTypes(types.StringType), types.UintType,
decls.OverloadExamples(`uint('123') // 123u`),
decls.UnaryBinding(convertToType(types.UintType)))),
// String functions
function(overloads.Contains,
decls.FunctionDocs(`test whether a string contains a substring`),
decls.MemberOverload(overloads.ContainsString,
argTypes(types.StringType, types.StringType), types.BoolType,
decls.OverloadExamples(
`'hello world'.contains('o w') // true`,
`'hello world'.contains('goodbye') // false`),
decls.BinaryBinding(types.StringContains)),
decls.DisableTypeGuards(true)),
function(overloads.EndsWith,
decls.FunctionDocs(`test whether a string ends with a substring suffix`),
decls.MemberOverload(overloads.EndsWithString,
argTypes(types.StringType, types.StringType), types.BoolType,
decls.OverloadExamples(
`'hello world'.endsWith('world') // true`,
`'hello world'.endsWith('hello') // false`),
decls.BinaryBinding(types.StringEndsWith)),
decls.DisableTypeGuards(true)),
function(overloads.StartsWith,
decls.FunctionDocs(`test whether a string starts with a substring prefix`),
decls.MemberOverload(overloads.StartsWithString,
argTypes(types.StringType, types.StringType), types.BoolType,
decls.OverloadExamples(
`'hello world'.startsWith('hello') // true`,
`'hello world'.startsWith('world') // false`),
decls.BinaryBinding(types.StringStartsWith)),
decls.DisableTypeGuards(true)),
function(overloads.Matches,
decls.FunctionDocs(`test whether a string matches an RE2 regular expression`),
decls.Overload(overloads.Matches, argTypes(types.StringType, types.StringType), types.BoolType,
decls.OverloadExamples(
`matches('123-456', '^[0-9]+(-[0-9]+)?$') // true`,
`matches('hello', '^h.*o$') // true`)),
decls.MemberOverload(overloads.MatchesString,
argTypes(types.StringType, types.StringType), types.BoolType,
decls.OverloadExamples(
`'123-456'.matches('^[0-9]+(-[0-9]+)?$') // true`,
`'hello'.matches('^h.*o$') // true`)),
decls.SingletonBinaryBinding(func(str, pat ref.Val) ref.Val {
return str.(traits.Matcher).Match(pat)
}, traits.MatcherType)),
// Timestamp / duration functions
function(overloads.TimeGetFullYear,
decls.FunctionDocs(`get the 0-based full year from a timestamp, UTC unless an IANA timezone is specified.`),
decls.MemberOverload(overloads.TimestampToYear,
argTypes(types.TimestampType), types.IntType,
decls.OverloadExamples(`timestamp('2023-07-14T10:30:45.123Z').getFullYear() // 2023`),
decls.UnaryBinding(func(ts ref.Val) ref.Val {
return timestampGetFullYear(ts, utcTZ)
})),
decls.MemberOverload(overloads.TimestampToYearWithTz,
argTypes(types.TimestampType, types.StringType), types.IntType,
decls.OverloadExamples(`timestamp('2023-01-01T05:30:00Z').getFullYear('-08:00') // 2022`),
decls.BinaryBinding(timestampGetFullYear))),
function(overloads.TimeGetMonth,
decls.FunctionDocs(`get the 0-based month from a timestamp, UTC unless an IANA timezone is specified.`),
decls.MemberOverload(overloads.TimestampToMonth,
argTypes(types.TimestampType), types.IntType,
decls.OverloadExamples(`timestamp('2023-07-14T10:30:45.123Z').getMonth() // 6`), // July is month 6
decls.UnaryBinding(func(ts ref.Val) ref.Val {
return timestampGetMonth(ts, utcTZ)
})),
decls.MemberOverload(overloads.TimestampToMonthWithTz,
argTypes(types.TimestampType, types.StringType), types.IntType,
decls.OverloadExamples(`timestamp('2023-01-01T05:30:00Z').getMonth('America/Los_Angeles') // 11`), // December is month 11
decls.BinaryBinding(timestampGetMonth))),
function(overloads.TimeGetDayOfYear,
decls.FunctionDocs(`get the 0-based day of the year from a timestamp, UTC unless an IANA timezone is specified.`),
decls.MemberOverload(overloads.TimestampToDayOfYear,
argTypes(types.TimestampType), types.IntType,
decls.OverloadExamples(`timestamp('2023-01-02T00:00:00Z').getDayOfYear() // 1`),
decls.UnaryBinding(func(ts ref.Val) ref.Val {
return timestampGetDayOfYear(ts, utcTZ)
})),
decls.MemberOverload(overloads.TimestampToDayOfYearWithTz,
argTypes(types.TimestampType, types.StringType), types.IntType,
decls.OverloadExamples(`timestamp('2023-01-01T05:00:00Z').getDayOfYear('America/Los_Angeles') // 364`),
decls.BinaryBinding(timestampGetDayOfYear))),
function(overloads.TimeGetDayOfMonth,
decls.FunctionDocs(`get the 0-based day of the month from a timestamp, UTC unless an IANA timezone is specified.`),
decls.MemberOverload(overloads.TimestampToDayOfMonthZeroBased,
argTypes(types.TimestampType), types.IntType,
decls.OverloadExamples(`timestamp('2023-07-14T10:30:45.123Z').getDayOfMonth() // 13`),
decls.UnaryBinding(func(ts ref.Val) ref.Val {
return timestampGetDayOfMonthZeroBased(ts, utcTZ)
})),
decls.MemberOverload(overloads.TimestampToDayOfMonthZeroBasedWithTz,
argTypes(types.TimestampType, types.StringType), types.IntType,
decls.OverloadExamples(`timestamp('2023-07-01T05:00:00Z').getDayOfMonth('America/Los_Angeles') // 29`),
decls.BinaryBinding(timestampGetDayOfMonthZeroBased))),
function(overloads.TimeGetDate,
decls.FunctionDocs(`get the 1-based day of the month from a timestamp, UTC unless an IANA timezone is specified.`),
decls.MemberOverload(overloads.TimestampToDayOfMonthOneBased,
argTypes(types.TimestampType), types.IntType,
decls.OverloadExamples(`timestamp('2023-07-14T10:30:45.123Z').getDate() // 14`),
decls.UnaryBinding(func(ts ref.Val) ref.Val {
return timestampGetDayOfMonthOneBased(ts, utcTZ)
})),
decls.MemberOverload(overloads.TimestampToDayOfMonthOneBasedWithTz,
argTypes(types.TimestampType, types.StringType), types.IntType,
decls.OverloadExamples(`timestamp('2023-07-01T05:00:00Z').getDate('America/Los_Angeles') // 30`),
decls.BinaryBinding(timestampGetDayOfMonthOneBased))),
function(overloads.TimeGetDayOfWeek,
decls.FunctionDocs(`get the 0-based day of the week from a timestamp, UTC unless an IANA timezone is specified.`),
decls.MemberOverload(overloads.TimestampToDayOfWeek,
argTypes(types.TimestampType), types.IntType,
decls.OverloadExamples(`timestamp('2023-07-14T10:30:45.123Z').getDayOfWeek() // 5`), // Friday is day 5
decls.UnaryBinding(func(ts ref.Val) ref.Val {
return timestampGetDayOfWeek(ts, utcTZ)
})),
decls.MemberOverload(overloads.TimestampToDayOfWeekWithTz,
argTypes(types.TimestampType, types.StringType), types.IntType,
decls.OverloadExamples(`timestamp('2023-07-16T05:00:00Z').getDayOfWeek('America/Los_Angeles') // 6`), // Saturday is day 6
decls.BinaryBinding(timestampGetDayOfWeek))),
function(overloads.TimeGetHours,
decls.FunctionDocs(`get the hours portion from a timestamp, or convert a duration to hours`),
decls.MemberOverload(overloads.TimestampToHours,
argTypes(types.TimestampType), types.IntType,
decls.OverloadExamples(`timestamp('2023-07-14T10:30:45.123Z').getHours() // 10`),
decls.UnaryBinding(func(ts ref.Val) ref.Val {
return timestampGetHours(ts, utcTZ)
})),
decls.MemberOverload(overloads.TimestampToHoursWithTz,
argTypes(types.TimestampType, types.StringType), types.IntType,
decls.OverloadExamples(`timestamp('2023-07-14T10:30:45.123Z').getHours('America/Los_Angeles') // 2`),
decls.BinaryBinding(timestampGetHours)),
decls.MemberOverload(overloads.DurationToHours,
argTypes(types.DurationType), types.IntType,
decls.OverloadExamples(`duration('3723s').getHours() // 1`),
decls.UnaryBinding(types.DurationGetHours))),
function(overloads.TimeGetMinutes,
decls.FunctionDocs(`get the minutes portion from a timestamp, or convert a duration to minutes`),
decls.MemberOverload(overloads.TimestampToMinutes,
argTypes(types.TimestampType), types.IntType,
decls.OverloadExamples(`timestamp('2023-07-14T10:30:45.123Z').getMinutes() // 30`),
decls.UnaryBinding(func(ts ref.Val) ref.Val {
return timestampGetMinutes(ts, utcTZ)
})),
decls.MemberOverload(overloads.TimestampToMinutesWithTz,
argTypes(types.TimestampType, types.StringType), types.IntType,
decls.OverloadExamples(`timestamp('2023-07-14T10:30:45.123Z').getMinutes('America/Los_Angeles') // 30`),
decls.BinaryBinding(timestampGetMinutes)),
decls.MemberOverload(overloads.DurationToMinutes,
argTypes(types.DurationType), types.IntType,
decls.OverloadExamples(`duration('3723s').getMinutes() // 62`),
decls.UnaryBinding(types.DurationGetMinutes))),
function(overloads.TimeGetSeconds,
decls.FunctionDocs(`get the seconds portion from a timestamp, or convert a duration to seconds`),
decls.MemberOverload(overloads.TimestampToSeconds,
argTypes(types.TimestampType), types.IntType,
decls.OverloadExamples(`timestamp('2023-07-14T10:30:45.123Z').getSeconds() // 45`),
decls.UnaryBinding(func(ts ref.Val) ref.Val {
return timestampGetSeconds(ts, utcTZ)
})),
decls.MemberOverload(overloads.TimestampToSecondsWithTz,
argTypes(types.TimestampType, types.StringType), types.IntType,
decls.OverloadExamples(`timestamp('2023-07-14T10:30:45.123Z').getSeconds('America/Los_Angeles') // 45`),
decls.BinaryBinding(timestampGetSeconds)),
decls.MemberOverload(overloads.DurationToSeconds,
argTypes(types.DurationType), types.IntType,
decls.OverloadExamples(`duration('3723.456s').getSeconds() // 3723`),
decls.UnaryBinding(types.DurationGetSeconds))),
function(overloads.TimeGetMilliseconds,
decls.FunctionDocs(`get the milliseconds portion from a timestamp`),
decls.MemberOverload(overloads.TimestampToMilliseconds,
argTypes(types.TimestampType), types.IntType,
decls.OverloadExamples(`timestamp('2023-07-14T10:30:45.123Z').getMilliseconds() // 123`),
decls.UnaryBinding(func(ts ref.Val) ref.Val {
return timestampGetMilliseconds(ts, utcTZ)
})),
decls.MemberOverload(overloads.TimestampToMillisecondsWithTz,
argTypes(types.TimestampType, types.StringType), types.IntType,
decls.OverloadExamples(`timestamp('2023-07-14T10:30:45.123Z').getMilliseconds('America/Los_Angeles') // 123`),
decls.BinaryBinding(timestampGetMilliseconds)),
decls.MemberOverload(overloads.DurationToMilliseconds,
argTypes(types.DurationType), types.IntType,
decls.UnaryBinding(types.DurationGetMilliseconds))),
}
}
// Functions returns the set of standard library function declarations and definitions for CEL.
func Functions() []*decls.FunctionDecl {
return stdFunctions
}
// Types returns the set of standard library types for CEL.
func Types() []*decls.VariableDecl {
return stdTypes
}
func notStrictlyFalse(value ref.Val) ref.Val {
if types.IsBool(value) {
return value
}
return types.True
}
func inAggregate(lhs ref.Val, rhs ref.Val) ref.Val {
if rhs.Type().HasTrait(traits.ContainerType) {
return rhs.(traits.Container).Contains(lhs)
}
return types.ValOrErr(rhs, "no such overload")
}
func function(name string, opts ...decls.FunctionOpt) *decls.FunctionDecl {
fn, err := decls.NewFunction(name, opts...)
if err != nil {
panic(err)
}
return fn
}
func argTypes(args ...*types.Type) []*types.Type {
return args
}
func noBinaryOverrides(rhs, lhs ref.Val) ref.Val {
return types.NoSuchOverloadErr()
}
func noFunctionOverrides(args ...ref.Val) ref.Val {
return types.NoSuchOverloadErr()
}
func identity(val ref.Val) ref.Val {
return val
}
func convertToType(t ref.Type) functions.UnaryOp {
return func(val ref.Val) ref.Val {
return val.ConvertToType(t)
}
}
func timestampGetFullYear(ts, tz ref.Val) ref.Val {
t, err := inTimeZone(ts, tz)
if err != nil {
return types.NewErrFromString(err.Error())
}
return types.Int(t.Year())
}
func timestampGetMonth(ts, tz ref.Val) ref.Val {
t, err := inTimeZone(ts, tz)
if err != nil {
return types.NewErrFromString(err.Error())
}
// CEL spec indicates that the month should be 0-based, but the Time value
// for Month() is 1-based.
return types.Int(t.Month() - 1)
}
func timestampGetDayOfYear(ts, tz ref.Val) ref.Val {
t, err := inTimeZone(ts, tz)
if err != nil {
return types.NewErrFromString(err.Error())
}
return types.Int(t.YearDay() - 1)
}
func timestampGetDayOfMonthZeroBased(ts, tz ref.Val) ref.Val {
t, err := inTimeZone(ts, tz)
if err != nil {
return types.NewErrFromString(err.Error())
}
return types.Int(t.Day() - 1)
}
func timestampGetDayOfMonthOneBased(ts, tz ref.Val) ref.Val {
t, err := inTimeZone(ts, tz)
if err != nil {
return types.NewErrFromString(err.Error())
}
return types.Int(t.Day())
}
func timestampGetDayOfWeek(ts, tz ref.Val) ref.Val {
t, err := inTimeZone(ts, tz)
if err != nil {
return types.NewErrFromString(err.Error())
}
return types.Int(t.Weekday())
}
func timestampGetHours(ts, tz ref.Val) ref.Val {
t, err := inTimeZone(ts, tz)
if err != nil {
return types.NewErrFromString(err.Error())
}
return types.Int(t.Hour())
}
func timestampGetMinutes(ts, tz ref.Val) ref.Val {
t, err := inTimeZone(ts, tz)
if err != nil {
return types.NewErrFromString(err.Error())
}
return types.Int(t.Minute())
}
func timestampGetSeconds(ts, tz ref.Val) ref.Val {
t, err := inTimeZone(ts, tz)
if err != nil {
return types.NewErrFromString(err.Error())
}
return types.Int(t.Second())
}
func timestampGetMilliseconds(ts, tz ref.Val) ref.Val {
t, err := inTimeZone(ts, tz)
if err != nil {
return types.NewErrFromString(err.Error())
}
return types.Int(t.Nanosecond() / 1000000)
}
func inTimeZone(ts, tz ref.Val) (time.Time, error) {
t := ts.(types.Timestamp)
val := string(tz.(types.String))
ind := strings.Index(val, ":")
if ind == -1 {
loc, err := time.LoadLocation(val)
if err != nil {
return time.Time{}, err
}
return t.In(loc), nil
}
// If the input is not the name of a timezone (for example, 'US/Central'), it should be a numerical offset from UTC
// in the format ^(+|-)(0[0-9]|1[0-4]):[0-5][0-9]$. The numerical input is parsed in terms of hours and minutes.
hr, err := strconv.Atoi(string(val[0:ind]))
if err != nil {
return time.Time{}, err
}
min, err := strconv.Atoi(string(val[ind+1:]))
if err != nil {
return time.Time{}, err
}
var offset int
if string(val[0]) == "-" {
offset = hr*60 - min
} else {
offset = hr*60 + min
}
secondsEastOfUTC := int((time.Duration(offset) * time.Minute).Seconds())
timezone := time.FixedZone("", secondsEastOfUTC)
return t.In(timezone), nil
}
// Copyright 2018 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package types
import (
"fmt"
"reflect"
"strconv"
"strings"
"github.com/google/cel-go/common/types/ref"
anypb "google.golang.org/protobuf/types/known/anypb"
structpb "google.golang.org/protobuf/types/known/structpb"
wrapperspb "google.golang.org/protobuf/types/known/wrapperspb"
)
// Bool type that implements ref.Val and supports comparison and negation.
type Bool bool
var (
// boolWrapperType golang reflected type for protobuf bool wrapper type.
boolWrapperType = reflect.TypeOf(&wrapperspb.BoolValue{})
)
// Boolean constants
const (
False = Bool(false)
True = Bool(true)
)
// Compare implements the traits.Comparer interface method.
func (b Bool) Compare(other ref.Val) ref.Val {
otherBool, ok := other.(Bool)
if !ok {
return ValOrErr(other, "no such overload")
}
if b == otherBool {
return IntZero
}
if !b && otherBool {
return IntNegOne
}
return IntOne
}
// ConvertToNative implements the ref.Val interface method.
func (b Bool) ConvertToNative(typeDesc reflect.Type) (any, error) {
switch typeDesc.Kind() {
case reflect.Bool:
return reflect.ValueOf(b).Convert(typeDesc).Interface(), nil
case reflect.Ptr:
switch typeDesc {
case anyValueType:
// Primitives must be wrapped to a wrapperspb.BoolValue before being packed into an Any.
return anypb.New(wrapperspb.Bool(bool(b)))
case boolWrapperType:
// Convert the bool to a wrapperspb.BoolValue.
return wrapperspb.Bool(bool(b)), nil
case jsonValueType:
// Return the bool as a new structpb.Value.
return structpb.NewBoolValue(bool(b)), nil
default:
if typeDesc.Elem().Kind() == reflect.Bool {
p := bool(b)
return &p, nil
}
}
case reflect.Interface:
bv := b.Value()
if reflect.TypeOf(bv).Implements(typeDesc) {
return bv, nil
}
if reflect.TypeOf(b).Implements(typeDesc) {
return b, nil
}
}
return nil, fmt.Errorf("type conversion error from bool to '%v'", typeDesc)
}
// ConvertToType implements the ref.Val interface method.
func (b Bool) ConvertToType(typeVal ref.Type) ref.Val {
switch typeVal {
case StringType:
return String(strconv.FormatBool(bool(b)))
case BoolType:
return b
case TypeType:
return BoolType
}
return NewErr("type conversion error from '%v' to '%v'", BoolType, typeVal)
}
// Equal implements the ref.Val interface method.
func (b Bool) Equal(other ref.Val) ref.Val {
otherBool, ok := other.(Bool)
return Bool(ok && b == otherBool)
}
// IsZeroValue returns true if the boolean value is false.
func (b Bool) IsZeroValue() bool {
return b == False
}
// Negate implements the traits.Negater interface method.
func (b Bool) Negate() ref.Val {
return !b
}
// Type implements the ref.Val interface method.
func (b Bool) Type() ref.Type {
return BoolType
}
// Value implements the ref.Val interface method.
func (b Bool) Value() any {
return bool(b)
}
func (b Bool) format(sb *strings.Builder) {
if b {
sb.WriteString("true")
} else {
sb.WriteString("false")
}
}
// IsBool returns whether the input ref.Val or ref.Type is equal to BoolType.
func IsBool(elem ref.Val) bool {
switch v := elem.(type) {
case Bool:
return true
case ref.Val:
return v.Type() == BoolType
default:
return false
}
}
// Copyright 2018 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package types
import (
"bytes"
"encoding/base64"
"fmt"
"reflect"
"strings"
"unicode/utf8"
"github.com/google/cel-go/common/types/ref"
anypb "google.golang.org/protobuf/types/known/anypb"
structpb "google.golang.org/protobuf/types/known/structpb"
wrapperspb "google.golang.org/protobuf/types/known/wrapperspb"
)
// Bytes type that implements ref.Val and supports add, compare, and size
// operations.
type Bytes []byte
var (
// byteWrapperType golang reflected type for protobuf bytes wrapper type.
byteWrapperType = reflect.TypeOf(&wrapperspb.BytesValue{})
)
// Add implements traits.Adder interface method by concatenating byte sequences.
func (b Bytes) Add(other ref.Val) ref.Val {
otherBytes, ok := other.(Bytes)
if !ok {
return ValOrErr(other, "no such overload")
}
return append(b, otherBytes...)
}
// Compare implements traits.Comparer interface method by lexicographic ordering.
func (b Bytes) Compare(other ref.Val) ref.Val {
otherBytes, ok := other.(Bytes)
if !ok {
return ValOrErr(other, "no such overload")
}
return Int(bytes.Compare(b, otherBytes))
}
// ConvertToNative implements the ref.Val interface method.
func (b Bytes) ConvertToNative(typeDesc reflect.Type) (any, error) {
switch typeDesc.Kind() {
case reflect.Array:
if len(b) != typeDesc.Len() {
return nil, fmt.Errorf("[%d]byte not assignable to [%d]byte array", len(b), typeDesc.Len())
}
refArrPtr := reflect.New(reflect.ArrayOf(len(b), typeDesc.Elem()))
refArr := refArrPtr.Elem()
for i, byt := range b {
refArr.Index(i).Set(reflect.ValueOf(byt).Convert(typeDesc.Elem()))
}
return refArr.Interface(), nil
case reflect.Slice:
return reflect.ValueOf(b).Convert(typeDesc).Interface(), nil
case reflect.Ptr:
switch typeDesc {
case anyValueType:
// Primitives must be wrapped before being set on an Any field.
return anypb.New(wrapperspb.Bytes([]byte(b)))
case byteWrapperType:
// Convert the bytes to a wrapperspb.BytesValue.
return wrapperspb.Bytes([]byte(b)), nil
case jsonValueType:
// CEL follows the proto3 to JSON conversion by encoding bytes to a string via base64.
// The encoding below matches the golang 'encoding/json' behavior during marshaling,
// which uses base64.StdEncoding.
str := base64.StdEncoding.EncodeToString([]byte(b))
return structpb.NewStringValue(str), nil
}
case reflect.Interface:
bv := b.Value()
if reflect.TypeOf(bv).Implements(typeDesc) {
return bv, nil
}
if reflect.TypeOf(b).Implements(typeDesc) {
return b, nil
}
}
return nil, fmt.Errorf("type conversion error from Bytes to '%v'", typeDesc)
}
// ConvertToType implements the ref.Val interface method.
func (b Bytes) ConvertToType(typeVal ref.Type) ref.Val {
switch typeVal {
case StringType:
if !utf8.Valid(b) {
return NewErr("invalid UTF-8 in bytes, cannot convert to string")
}
return String(b)
case BytesType:
return b
case TypeType:
return BytesType
}
return NewErr("type conversion error from '%s' to '%s'", BytesType, typeVal)
}
// Equal implements the ref.Val interface method.
func (b Bytes) Equal(other ref.Val) ref.Val {
otherBytes, ok := other.(Bytes)
return Bool(ok && bytes.Equal(b, otherBytes))
}
// IsZeroValue returns true if the byte array is empty.
func (b Bytes) IsZeroValue() bool {
return len(b) == 0
}
// Size implements the traits.Sizer interface method.
func (b Bytes) Size() ref.Val {
return Int(len(b))
}
// Type implements the ref.Val interface method.
func (b Bytes) Type() ref.Type {
return BytesType
}
// Value implements the ref.Val interface method.
func (b Bytes) Value() any {
return []byte(b)
}
func (b Bytes) format(sb *strings.Builder) {
fmt.Fprintf(sb, "b\"%s\"", bytesToOctets([]byte(b)))
}
// bytesToOctets converts byte sequences to a string using a three digit octal encoded value
// per byte.
func bytesToOctets(byteVal []byte) string {
var b strings.Builder
for _, c := range byteVal {
fmt.Fprintf(&b, "\\%03o", c)
}
return b.String()
}
// Copyright 2021 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package types
import (
"math"
"github.com/google/cel-go/common/types/ref"
)
func compareDoubleInt(d Double, i Int) Int {
if d < math.MinInt64 {
return IntNegOne
}
if d > math.MaxInt64 {
return IntOne
}
return compareDouble(d, Double(i))
}
func compareIntDouble(i Int, d Double) Int {
return -compareDoubleInt(d, i)
}
func compareDoubleUint(d Double, u Uint) Int {
if d < 0 {
return IntNegOne
}
if d > math.MaxUint64 {
return IntOne
}
return compareDouble(d, Double(u))
}
func compareUintDouble(u Uint, d Double) Int {
return -compareDoubleUint(d, u)
}
func compareIntUint(i Int, u Uint) Int {
if i < 0 || u > math.MaxInt64 {
return IntNegOne
}
cmp := i - Int(u)
if cmp < 0 {
return IntNegOne
}
if cmp > 0 {
return IntOne
}
return IntZero
}
func compareUintInt(u Uint, i Int) Int {
return -compareIntUint(i, u)
}
func compareDouble(a, b Double) Int {
if a < b {
return IntNegOne
}
if a > b {
return IntOne
}
return IntZero
}
func compareInt(a, b Int) ref.Val {
if a < b {
return IntNegOne
}
if a > b {
return IntOne
}
return IntZero
}
func compareUint(a, b Uint) ref.Val {
if a < b {
return IntNegOne
}
if a > b {
return IntOne
}
return IntZero
}
// Copyright 2018 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package types
import (
"fmt"
"math"
"reflect"
"strconv"
"strings"
"github.com/google/cel-go/common/types/ref"
anypb "google.golang.org/protobuf/types/known/anypb"
structpb "google.golang.org/protobuf/types/known/structpb"
wrapperspb "google.golang.org/protobuf/types/known/wrapperspb"
)
// Double type that implements ref.Val, comparison, and mathematical
// operations.
type Double float64
var (
// doubleWrapperType reflected type for protobuf double wrapper type.
doubleWrapperType = reflect.TypeOf(&wrapperspb.DoubleValue{})
// floatWrapperType reflected type for protobuf float wrapper type.
floatWrapperType = reflect.TypeOf(&wrapperspb.FloatValue{})
)
// Add implements traits.Adder.Add.
func (d Double) Add(other ref.Val) ref.Val {
otherDouble, ok := other.(Double)
if !ok {
return MaybeNoSuchOverloadErr(other)
}
return d + otherDouble
}
// Compare implements traits.Comparer.Compare.
func (d Double) Compare(other ref.Val) ref.Val {
if math.IsNaN(float64(d)) {
return NewErr("NaN values cannot be ordered")
}
switch ov := other.(type) {
case Double:
if math.IsNaN(float64(ov)) {
return NewErr("NaN values cannot be ordered")
}
return compareDouble(d, ov)
case Int:
return compareDoubleInt(d, ov)
case Uint:
return compareDoubleUint(d, ov)
default:
return MaybeNoSuchOverloadErr(other)
}
}
// ConvertToNative implements ref.Val.ConvertToNative.
func (d Double) ConvertToNative(typeDesc reflect.Type) (any, error) {
switch typeDesc.Kind() {
case reflect.Float32:
v := float32(d)
return reflect.ValueOf(v).Convert(typeDesc).Interface(), nil
case reflect.Float64:
v := float64(d)
return reflect.ValueOf(v).Convert(typeDesc).Interface(), nil
case reflect.Ptr:
switch typeDesc {
case anyValueType:
// Primitives must be wrapped before being set on an Any field.
return anypb.New(wrapperspb.Double(float64(d)))
case doubleWrapperType:
// Convert to a wrapperspb.DoubleValue
return wrapperspb.Double(float64(d)), nil
case floatWrapperType:
// Convert to a wrapperspb.FloatValue (with truncation).
return wrapperspb.Float(float32(d)), nil
case jsonValueType:
// Note, there are special cases for proto3 to json conversion that
// expect the floating point value to be converted to a NaN,
// Infinity, or -Infinity string values, but the jsonpb string
// marshaling of the protobuf.Value will handle this conversion.
return structpb.NewNumberValue(float64(d)), nil
}
switch typeDesc.Elem().Kind() {
case reflect.Float32:
v := float32(d)
p := reflect.New(typeDesc.Elem())
p.Elem().Set(reflect.ValueOf(v).Convert(typeDesc.Elem()))
return p.Interface(), nil
case reflect.Float64:
v := float64(d)
p := reflect.New(typeDesc.Elem())
p.Elem().Set(reflect.ValueOf(v).Convert(typeDesc.Elem()))
return p.Interface(), nil
}
case reflect.Interface:
dv := d.Value()
if reflect.TypeOf(dv).Implements(typeDesc) {
return dv, nil
}
if reflect.TypeOf(d).Implements(typeDesc) {
return d, nil
}
}
return nil, fmt.Errorf("type conversion error from Double to '%v'", typeDesc)
}
// ConvertToType implements ref.Val.ConvertToType.
func (d Double) ConvertToType(typeVal ref.Type) ref.Val {
switch typeVal {
case IntType:
i, err := doubleToInt64Checked(float64(d))
if err != nil {
return WrapErr(err)
}
return Int(i)
case UintType:
i, err := doubleToUint64Checked(float64(d))
if err != nil {
return WrapErr(err)
}
return Uint(i)
case DoubleType:
return d
case StringType:
return String(fmt.Sprintf("%g", float64(d)))
case TypeType:
return DoubleType
}
return NewErr("type conversion error from '%s' to '%s'", DoubleType, typeVal)
}
// Divide implements traits.Divider.Divide.
func (d Double) Divide(other ref.Val) ref.Val {
otherDouble, ok := other.(Double)
if !ok {
return MaybeNoSuchOverloadErr(other)
}
return d / otherDouble
}
// Equal implements ref.Val.Equal.
func (d Double) Equal(other ref.Val) ref.Val {
if math.IsNaN(float64(d)) {
return False
}
switch ov := other.(type) {
case Double:
if math.IsNaN(float64(ov)) {
return False
}
return Bool(d == ov)
case Int:
return Bool(compareDoubleInt(d, ov) == 0)
case Uint:
return Bool(compareDoubleUint(d, ov) == 0)
default:
return False
}
}
// IsZeroValue returns true if double value is 0.0
func (d Double) IsZeroValue() bool {
return float64(d) == 0.0
}
// Multiply implements traits.Multiplier.Multiply.
func (d Double) Multiply(other ref.Val) ref.Val {
otherDouble, ok := other.(Double)
if !ok {
return MaybeNoSuchOverloadErr(other)
}
return d * otherDouble
}
// Negate implements traits.Negater.Negate.
func (d Double) Negate() ref.Val {
return -d
}
// Subtract implements traits.Subtractor.Subtract.
func (d Double) Subtract(subtrahend ref.Val) ref.Val {
subtraDouble, ok := subtrahend.(Double)
if !ok {
return MaybeNoSuchOverloadErr(subtrahend)
}
return d - subtraDouble
}
// Type implements ref.Val.Type.
func (d Double) Type() ref.Type {
return DoubleType
}
// Value implements ref.Val.Value.
func (d Double) Value() any {
return float64(d)
}
func (d Double) format(sb *strings.Builder) {
if math.IsNaN(float64(d)) {
sb.WriteString(`double("NaN")`)
return
}
if math.IsInf(float64(d), -1) {
sb.WriteString(`double("-Infinity")`)
return
}
if math.IsInf(float64(d), 1) {
sb.WriteString(`double("Infinity")`)
return
}
s := strconv.FormatFloat(float64(d), 'f', -1, 64)
sb.WriteString(s)
if !strings.ContainsRune(s, '.') {
sb.WriteString(".0")
}
}
// Copyright 2018 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package types
import (
"fmt"
"reflect"
"strconv"
"strings"
"time"
"github.com/google/cel-go/common/overloads"
"github.com/google/cel-go/common/types/ref"
anypb "google.golang.org/protobuf/types/known/anypb"
dpb "google.golang.org/protobuf/types/known/durationpb"
structpb "google.golang.org/protobuf/types/known/structpb"
)
// Duration type that implements ref.Val and supports add, compare, negate,
// and subtract operators. This type is also a receiver which means it can
// participate in dispatch to receiver functions.
type Duration struct {
time.Duration
}
func durationOf(d time.Duration) Duration {
return Duration{Duration: d}
}
var (
durationValueType = reflect.TypeOf(&dpb.Duration{})
durationZeroArgOverloads = map[string]func(ref.Val) ref.Val{
overloads.TimeGetHours: DurationGetHours,
overloads.TimeGetMinutes: DurationGetMinutes,
overloads.TimeGetSeconds: DurationGetSeconds,
overloads.TimeGetMilliseconds: DurationGetMilliseconds,
}
)
// Add implements traits.Adder.Add.
func (d Duration) Add(other ref.Val) ref.Val {
switch other.Type() {
case DurationType:
dur2 := other.(Duration)
val, err := addDurationChecked(d.Duration, dur2.Duration)
if err != nil {
return WrapErr(err)
}
return durationOf(val)
case TimestampType:
ts := other.(Timestamp).Time
val, err := addTimeDurationChecked(ts, d.Duration)
if err != nil {
return WrapErr(err)
}
return timestampOf(val)
}
return MaybeNoSuchOverloadErr(other)
}
// Compare implements traits.Comparer.Compare.
func (d Duration) Compare(other ref.Val) ref.Val {
otherDur, ok := other.(Duration)
if !ok {
return MaybeNoSuchOverloadErr(other)
}
d1 := d.Duration
d2 := otherDur.Duration
switch {
case d1 < d2:
return IntNegOne
case d1 > d2:
return IntOne
default:
return IntZero
}
}
// ConvertToNative implements ref.Val.ConvertToNative.
func (d Duration) ConvertToNative(typeDesc reflect.Type) (any, error) {
// If the duration is already assignable to the desired type return it.
if reflect.TypeOf(d.Duration).AssignableTo(typeDesc) {
return d.Duration, nil
}
if reflect.TypeOf(d).AssignableTo(typeDesc) {
return d, nil
}
switch typeDesc {
case anyValueType:
// Pack the duration as a dpb.Duration into an Any value.
return anypb.New(dpb.New(d.Duration))
case durationValueType:
// Unwrap the CEL value to its underlying proto value.
return dpb.New(d.Duration), nil
case jsonValueType:
// CEL follows the proto3 to JSON conversion.
// Note, using jsonpb would wrap the result in extra double quotes.
v := d.ConvertToType(StringType)
if IsError(v) {
return nil, v.(*Err)
}
return structpb.NewStringValue(string(v.(String))), nil
}
return nil, fmt.Errorf("type conversion error from 'Duration' to '%v'", typeDesc)
}
// ConvertToType implements ref.Val.ConvertToType.
func (d Duration) ConvertToType(typeVal ref.Type) ref.Val {
switch typeVal {
case StringType:
return String(strconv.FormatFloat(d.Seconds(), 'f', -1, 64) + "s")
case IntType:
return Int(d.Duration)
case DurationType:
return d
case TypeType:
return DurationType
}
return NewErr("type conversion error from '%s' to '%s'", DurationType, typeVal)
}
// Equal implements ref.Val.Equal.
func (d Duration) Equal(other ref.Val) ref.Val {
otherDur, ok := other.(Duration)
return Bool(ok && d.Duration == otherDur.Duration)
}
// IsZeroValue returns true if the duration value is zero
func (d Duration) IsZeroValue() bool {
return d.Duration == 0
}
// Negate implements traits.Negater.Negate.
func (d Duration) Negate() ref.Val {
val, err := negateDurationChecked(d.Duration)
if err != nil {
return WrapErr(err)
}
return durationOf(val)
}
// Receive implements traits.Receiver.Receive.
func (d Duration) Receive(function string, overload string, args []ref.Val) ref.Val {
if len(args) == 0 {
if f, found := durationZeroArgOverloads[function]; found {
return f(d)
}
}
return NoSuchOverloadErr()
}
// Subtract implements traits.Subtractor.Subtract.
func (d Duration) Subtract(subtrahend ref.Val) ref.Val {
subtraDur, ok := subtrahend.(Duration)
if !ok {
return MaybeNoSuchOverloadErr(subtrahend)
}
val, err := subtractDurationChecked(d.Duration, subtraDur.Duration)
if err != nil {
return WrapErr(err)
}
return durationOf(val)
}
// Type implements ref.Val.Type.
func (d Duration) Type() ref.Type {
return DurationType
}
// Value implements ref.Val.Value.
func (d Duration) Value() any {
return d.Duration
}
func (d Duration) format(sb *strings.Builder) {
fmt.Fprintf(sb, `duration("%ss")`, strconv.FormatFloat(d.Seconds(), 'f', -1, 64))
}
// DurationGetHours returns the duration in hours.
func DurationGetHours(val ref.Val) ref.Val {
dur, ok := val.(Duration)
if !ok {
return MaybeNoSuchOverloadErr(val)
}
return Int(dur.Hours())
}
// DurationGetMinutes returns duration in minutes.
func DurationGetMinutes(val ref.Val) ref.Val {
dur, ok := val.(Duration)
if !ok {
return MaybeNoSuchOverloadErr(val)
}
return Int(dur.Minutes())
}
// DurationGetSeconds returns duration in seconds.
func DurationGetSeconds(val ref.Val) ref.Val {
dur, ok := val.(Duration)
if !ok {
return MaybeNoSuchOverloadErr(val)
}
return Int(dur.Seconds())
}
// DurationGetMilliseconds returns duration in milliseconds.
func DurationGetMilliseconds(val ref.Val) ref.Val {
dur, ok := val.(Duration)
if !ok {
return MaybeNoSuchOverloadErr(val)
}
return Int(dur.Milliseconds())
}
// Copyright 2018 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package types
import (
"errors"
"fmt"
"reflect"
"github.com/google/cel-go/common/types/ref"
)
// Error interface which allows types types.Err values to be treated as error values.
type Error interface {
error
ref.Val
}
// Err type which extends the built-in go error and implements ref.Val.
type Err struct {
error
id int64
}
var (
// ErrType singleton.
ErrType = NewOpaqueType("error")
// errDivideByZero is an error indicating a division by zero of an integer value.
errDivideByZero = errors.New("division by zero")
// errModulusByZero is an error indicating a modulus by zero of an integer value.
errModulusByZero = errors.New("modulus by zero")
// errIntOverflow is an error representing integer overflow.
errIntOverflow = errors.New("integer overflow")
// errUintOverflow is an error representing unsigned integer overflow.
errUintOverflow = errors.New("unsigned integer overflow")
// errDurationOverflow is an error representing duration overflow.
errDurationOverflow = errors.New("duration overflow")
// errTimestampOverflow is an error representing timestamp overflow.
errTimestampOverflow = errors.New("timestamp overflow")
celErrTimestampOverflow = &Err{error: errTimestampOverflow}
// celErrNoSuchOverload indicates that the call arguments did not match a supported method signature.
celErrNoSuchOverload = NewErr("no such overload")
)
// NewErr creates a new Err described by the format string and args.
// TODO: Audit the use of this function and standardize the error messages and codes.
func NewErr(format string, args ...any) ref.Val {
return &Err{error: fmt.Errorf(format, args...)}
}
// NewErrFromString creates a new Err with the provided message.
// TODO: Audit the use of this function and standardize the error messages and codes.
func NewErrFromString(message string) ref.Val {
return &Err{error: errors.New(message)}
}
// NewErrWithNodeID creates a new Err described by the format string and args.
// TODO: Audit the use of this function and standardize the error messages and codes.
func NewErrWithNodeID(id int64, format string, args ...any) ref.Val {
return &Err{error: fmt.Errorf(format, args...), id: id}
}
// LabelErrNode returns val unaltered it is not an Err or if the error has a non-zero
// AST node ID already present. Otherwise the id is added to the error for
// recovery with the Err.NodeID method.
func LabelErrNode(id int64, val ref.Val) ref.Val {
if err, ok := val.(*Err); ok && err.id == 0 {
err.id = id
return err
}
return val
}
// NoSuchOverloadErr returns a new types.Err instance with a no such overload message.
func NoSuchOverloadErr() ref.Val {
return celErrNoSuchOverload
}
// UnsupportedRefValConversionErr returns a types.NewErr instance with a no such conversion
// message that indicates that the native value could not be converted to a CEL ref.Val.
func UnsupportedRefValConversionErr(val any) ref.Val {
return NewErr("unsupported conversion to ref.Val: (%T)%v", val, val)
}
// MaybeNoSuchOverloadErr returns the error or unknown if the input ref.Val is one of these types,
// else a new no such overload error.
func MaybeNoSuchOverloadErr(val ref.Val) ref.Val {
return ValOrErr(val, "no such overload")
}
// ValOrErr either returns the existing error or creates a new one.
// TODO: Audit the use of this function and standardize the error messages and codes.
func ValOrErr(val ref.Val, format string, args ...any) ref.Val {
if val == nil || !IsUnknownOrError(val) {
return NewErr(format, args...)
}
return val
}
// WrapErr wraps an existing Go error value into a CEL Err value.
func WrapErr(err error) ref.Val {
return &Err{error: err}
}
// ConvertToNative implements ref.Val.ConvertToNative.
func (e *Err) ConvertToNative(typeDesc reflect.Type) (any, error) {
return nil, e.error
}
// ConvertToType implements ref.Val.ConvertToType.
func (e *Err) ConvertToType(typeVal ref.Type) ref.Val {
// Errors are not convertible to other representations.
return e
}
// Equal implements ref.Val.Equal.
func (e *Err) Equal(other ref.Val) ref.Val {
// An error cannot be equal to any other value, so it returns itself.
return e
}
// String implements fmt.Stringer.
func (e *Err) String() string {
return e.error.Error()
}
// Type implements ref.Val.Type.
func (e *Err) Type() ref.Type {
return ErrType
}
// Value implements ref.Val.Value.
func (e *Err) Value() any {
return e.error
}
// NodeID returns the AST node ID of the expression that returned the error.
func (e *Err) NodeID() int64 {
return e.id
}
// Is implements errors.Is.
func (e *Err) Is(target error) bool {
return e.error.Error() == target.Error()
}
// Unwrap implements errors.Unwrap.
func (e *Err) Unwrap() error {
return e.error
}
// IsError returns whether the input element ref.Type or ref.Val is equal to
// the ErrType singleton.
func IsError(val ref.Val) bool {
switch val.(type) {
case *Err:
return true
default:
return false
}
}
package types
import (
"fmt"
"strings"
"github.com/google/cel-go/common/types/ref"
"github.com/google/cel-go/common/types/traits"
)
type formattable interface {
format(*strings.Builder)
}
// Format formats the value as a string. The result is only intended for human consumption and ignores errors.
// Do not depend on the output being stable. It may change at any time.
func Format(val ref.Val) string {
var sb strings.Builder
formatTo(&sb, val)
return sb.String()
}
func formatTo(sb *strings.Builder, val ref.Val) {
if fmtable, ok := val.(formattable); ok {
fmtable.format(sb)
return
}
// All of the builtins implement formattable. Try to deal with traits.
if l, ok := val.(traits.Lister); ok {
formatList(l, sb)
return
}
if m, ok := val.(traits.Mapper); ok {
formatMap(m, sb)
return
}
// This could be an error, unknown, opaque or object.
// Unfortunately we have no consistent way of inspecting
// opaque and object. So we just fallback to fmt.Stringer
// and hope it is relavent.
fmt.Fprintf(sb, "%s", val)
}
// Copyright 2018 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package types
import (
"fmt"
"math"
"reflect"
"strconv"
"strings"
"time"
"github.com/google/cel-go/common/types/ref"
anypb "google.golang.org/protobuf/types/known/anypb"
structpb "google.golang.org/protobuf/types/known/structpb"
wrapperspb "google.golang.org/protobuf/types/known/wrapperspb"
)
// Int type that implements ref.Val as well as comparison and math operators.
type Int int64
// Int constants used for comparison results.
const (
// IntZero is the zero-value for Int
IntZero = Int(0)
IntOne = Int(1)
IntNegOne = Int(-1)
)
var (
// int32WrapperType reflected type for protobuf int32 wrapper type.
int32WrapperType = reflect.TypeOf(&wrapperspb.Int32Value{})
// int64WrapperType reflected type for protobuf int64 wrapper type.
int64WrapperType = reflect.TypeOf(&wrapperspb.Int64Value{})
)
// Add implements traits.Adder.Add.
func (i Int) Add(other ref.Val) ref.Val {
otherInt, ok := other.(Int)
if !ok {
return MaybeNoSuchOverloadErr(other)
}
val, err := addInt64Checked(int64(i), int64(otherInt))
if err != nil {
return WrapErr(err)
}
return Int(val)
}
// Compare implements traits.Comparer.Compare.
func (i Int) Compare(other ref.Val) ref.Val {
switch ov := other.(type) {
case Double:
if math.IsNaN(float64(ov)) {
return NewErr("NaN values cannot be ordered")
}
return compareIntDouble(i, ov)
case Int:
return compareInt(i, ov)
case Uint:
return compareIntUint(i, ov)
default:
return MaybeNoSuchOverloadErr(other)
}
}
// ConvertToNative implements ref.Val.ConvertToNative.
func (i Int) ConvertToNative(typeDesc reflect.Type) (any, error) {
switch typeDesc.Kind() {
case reflect.Int, reflect.Int32:
// Enums are also mapped as int32 derivations.
// Note, the code doesn't convert to the enum value directly since this is not known, but
// the net effect with respect to proto-assignment is handled correctly by the reflection
// Convert method.
v, err := int64ToInt32Checked(int64(i))
if err != nil {
return nil, err
}
return reflect.ValueOf(v).Convert(typeDesc).Interface(), nil
case reflect.Int8:
v, err := int64ToInt8Checked(int64(i))
if err != nil {
return nil, err
}
return reflect.ValueOf(v).Convert(typeDesc).Interface(), nil
case reflect.Int16:
v, err := int64ToInt16Checked(int64(i))
if err != nil {
return nil, err
}
return reflect.ValueOf(v).Convert(typeDesc).Interface(), nil
case reflect.Int64:
return reflect.ValueOf(i).Convert(typeDesc).Interface(), nil
case reflect.Ptr:
switch typeDesc {
case anyValueType:
// Primitives must be wrapped before being set on an Any field.
return anypb.New(wrapperspb.Int64(int64(i)))
case int32WrapperType:
// Convert the value to a wrapperspb.Int32Value, error on overflow.
v, err := int64ToInt32Checked(int64(i))
if err != nil {
return nil, err
}
return wrapperspb.Int32(v), nil
case int64WrapperType:
// Convert the value to a wrapperspb.Int64Value.
return wrapperspb.Int64(int64(i)), nil
case jsonValueType:
// The proto-to-JSON conversion rules would convert all 64-bit integer values to JSON
// decimal strings. Because CEL ints might come from the automatic widening of 32-bit
// values in protos, the JSON type is chosen dynamically based on the value.
//
// - Integers -2^53-1 < n < 2^53-1 are encoded as JSON numbers.
// - Integers outside this range are encoded as JSON strings.
//
// The integer to float range represents the largest interval where such a conversion
// can round-trip accurately. Thus, conversions from a 32-bit source can expect a JSON
// number as with protobuf. Those consuming JSON from a 64-bit source must be able to
// handle either a JSON number or a JSON decimal string. To handle these cases safely
// the string values must be explicitly converted to int() within a CEL expression;
// however, it is best to simply stay within the JSON number range when building JSON
// objects in CEL.
if i.isJSONSafe() {
return structpb.NewNumberValue(float64(i)), nil
}
// Proto3 to JSON conversion requires string-formatted int64 values
// since the conversion to floating point would result in truncation.
return structpb.NewStringValue(strconv.FormatInt(int64(i), 10)), nil
}
switch typeDesc.Elem().Kind() {
case reflect.Int32:
// Convert the value to a wrapperspb.Int32Value, error on overflow.
v, err := int64ToInt32Checked(int64(i))
if err != nil {
return nil, err
}
p := reflect.New(typeDesc.Elem())
p.Elem().Set(reflect.ValueOf(v).Convert(typeDesc.Elem()))
return p.Interface(), nil
case reflect.Int64:
v := int64(i)
p := reflect.New(typeDesc.Elem())
p.Elem().Set(reflect.ValueOf(v).Convert(typeDesc.Elem()))
return p.Interface(), nil
}
case reflect.Interface:
iv := i.Value()
if reflect.TypeOf(iv).Implements(typeDesc) {
return iv, nil
}
if reflect.TypeOf(i).Implements(typeDesc) {
return i, nil
}
}
return nil, fmt.Errorf("unsupported type conversion from 'int' to %v", typeDesc)
}
// ConvertToType implements ref.Val.ConvertToType.
func (i Int) ConvertToType(typeVal ref.Type) ref.Val {
switch typeVal {
case IntType:
return i
case UintType:
u, err := int64ToUint64Checked(int64(i))
if err != nil {
return WrapErr(err)
}
return Uint(u)
case DoubleType:
return Double(i)
case StringType:
return String(fmt.Sprintf("%d", int64(i)))
case TimestampType:
// The maximum positive value that can be passed to time.Unix is math.MaxInt64 minus the number
// of seconds between year 1 and year 1970. See comments on unixToInternal.
if int64(i) < minUnixTime || int64(i) > maxUnixTime {
return celErrTimestampOverflow
}
return timestampOf(time.Unix(int64(i), 0).UTC())
case TypeType:
return IntType
}
return NewErr("type conversion error from '%s' to '%s'", IntType, typeVal)
}
// Divide implements traits.Divider.Divide.
func (i Int) Divide(other ref.Val) ref.Val {
otherInt, ok := other.(Int)
if !ok {
return MaybeNoSuchOverloadErr(other)
}
val, err := divideInt64Checked(int64(i), int64(otherInt))
if err != nil {
return WrapErr(err)
}
return Int(val)
}
// Equal implements ref.Val.Equal.
func (i Int) Equal(other ref.Val) ref.Val {
switch ov := other.(type) {
case Double:
if math.IsNaN(float64(ov)) {
return False
}
return Bool(compareIntDouble(i, ov) == 0)
case Int:
return Bool(i == ov)
case Uint:
return Bool(compareIntUint(i, ov) == 0)
default:
return False
}
}
// IsZeroValue returns true if integer is equal to 0
func (i Int) IsZeroValue() bool {
return i == IntZero
}
// Modulo implements traits.Modder.Modulo.
func (i Int) Modulo(other ref.Val) ref.Val {
otherInt, ok := other.(Int)
if !ok {
return MaybeNoSuchOverloadErr(other)
}
val, err := moduloInt64Checked(int64(i), int64(otherInt))
if err != nil {
return WrapErr(err)
}
return Int(val)
}
// Multiply implements traits.Multiplier.Multiply.
func (i Int) Multiply(other ref.Val) ref.Val {
otherInt, ok := other.(Int)
if !ok {
return MaybeNoSuchOverloadErr(other)
}
val, err := multiplyInt64Checked(int64(i), int64(otherInt))
if err != nil {
return WrapErr(err)
}
return Int(val)
}
// Negate implements traits.Negater.Negate.
func (i Int) Negate() ref.Val {
val, err := negateInt64Checked(int64(i))
if err != nil {
return WrapErr(err)
}
return Int(val)
}
// Subtract implements traits.Subtractor.Subtract.
func (i Int) Subtract(subtrahend ref.Val) ref.Val {
subtraInt, ok := subtrahend.(Int)
if !ok {
return MaybeNoSuchOverloadErr(subtrahend)
}
val, err := subtractInt64Checked(int64(i), int64(subtraInt))
if err != nil {
return WrapErr(err)
}
return Int(val)
}
// Type implements ref.Val.Type.
func (i Int) Type() ref.Type {
return IntType
}
// Value implements ref.Val.Value.
func (i Int) Value() any {
return int64(i)
}
func (i Int) format(sb *strings.Builder) {
sb.WriteString(strconv.FormatInt(int64(i), 10))
}
// isJSONSafe indicates whether the int is safely representable as a floating point value in JSON.
func (i Int) isJSONSafe() bool {
return i >= minIntJSON && i <= maxIntJSON
}
const (
// maxIntJSON is defined as the Number.MAX_SAFE_INTEGER value per EcmaScript 6.
maxIntJSON = 1<<53 - 1
// minIntJSON is defined as the Number.MIN_SAFE_INTEGER value per EcmaScript 6.
minIntJSON = -maxIntJSON
)
// Copyright 2018 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package types
import (
"fmt"
"reflect"
"github.com/google/cel-go/common/types/ref"
"github.com/google/cel-go/common/types/traits"
)
var (
// IteratorType singleton.
IteratorType = NewObjectType("iterator", traits.IteratorType)
)
// baseIterator is the basis for list, map, and object iterators.
//
// An iterator in and of itself should not be a valid value for comparison, but must implement the
// `ref.Val` methods in order to be well-supported within instruction arguments processed by the
// interpreter.
type baseIterator struct{}
func (*baseIterator) ConvertToNative(typeDesc reflect.Type) (any, error) {
return nil, fmt.Errorf("type conversion on iterators not supported")
}
func (*baseIterator) ConvertToType(typeVal ref.Type) ref.Val {
return NewErr("no such overload")
}
func (*baseIterator) Equal(other ref.Val) ref.Val {
return NewErr("no such overload")
}
func (*baseIterator) Type() ref.Type {
return IteratorType
}
func (*baseIterator) Value() any {
return nil
}
// Copyright 2018 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package types
import (
"fmt"
"reflect"
"strings"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/reflect/protoreflect"
"github.com/google/cel-go/common/types/ref"
"github.com/google/cel-go/common/types/traits"
anypb "google.golang.org/protobuf/types/known/anypb"
structpb "google.golang.org/protobuf/types/known/structpb"
)
// NewDynamicList returns a traits.Lister with heterogenous elements.
// value should be an array of "native" types, i.e. any type that
// NativeToValue() can convert to a ref.Val.
func NewDynamicList(adapter Adapter, value any) traits.Lister {
refValue := reflect.ValueOf(value)
return &baseList{
Adapter: adapter,
value: value,
size: refValue.Len(),
get: func(i int) any {
return refValue.Index(i).Interface()
},
}
}
// NewStringList returns a traits.Lister containing only strings.
func NewStringList(adapter Adapter, elems []string) traits.Lister {
return &baseList{
Adapter: adapter,
value: elems,
size: len(elems),
get: func(i int) any { return elems[i] },
}
}
// NewRefValList returns a traits.Lister with ref.Val elements.
//
// This type specialization is used with list literals within CEL expressions.
func NewRefValList(adapter Adapter, elems []ref.Val) traits.Lister {
return &baseList{
Adapter: adapter,
value: elems,
size: len(elems),
get: func(i int) any { return elems[i] },
}
}
// NewProtoList returns a traits.Lister based on a pb.List instance.
func NewProtoList(adapter Adapter, list protoreflect.List) traits.Lister {
return &baseList{
Adapter: adapter,
value: list,
size: list.Len(),
get: func(i int) any { return list.Get(i).Interface() },
}
}
// NewJSONList returns a traits.Lister based on structpb.ListValue instance.
func NewJSONList(adapter Adapter, l *structpb.ListValue) traits.Lister {
vals := l.GetValues()
return &baseList{
Adapter: adapter,
value: l,
size: len(vals),
get: func(i int) any { return vals[i] },
}
}
// NewMutableList creates a new mutable list whose internal state can be modified.
func NewMutableList(adapter Adapter) traits.MutableLister {
var mutableValues []ref.Val
l := &mutableList{
baseList: &baseList{
Adapter: adapter,
value: mutableValues,
size: 0,
},
mutableValues: mutableValues,
}
l.get = func(i int) any {
return l.mutableValues[i]
}
return l
}
// baseList points to a list containing elements of any type.
// The `value` is an array of native values, and refValue is its reflection object.
// The `Adapter` enables native type to CEL type conversions.
type baseList struct {
Adapter
value any
// size indicates the number of elements within the list.
// Since objects are immutable the size of a list is static.
size int
// get returns a value at the specified integer index.
// The index is guaranteed to be checked against the list index range.
get func(int) any
}
// Add implements the traits.Adder interface method.
func (l *baseList) Add(other ref.Val) ref.Val {
otherList, ok := other.(traits.Lister)
if !ok {
return MaybeNoSuchOverloadErr(other)
}
if l.Size() == IntZero {
return other
}
if otherList.Size() == IntZero {
return l
}
return &concatList{
Adapter: l.Adapter,
prevList: l,
nextList: otherList}
}
// Contains implements the traits.Container interface method.
func (l *baseList) Contains(elem ref.Val) ref.Val {
for i := 0; i < l.size; i++ {
val := l.NativeToValue(l.get(i))
cmp := elem.Equal(val)
b, ok := cmp.(Bool)
if ok && b == True {
return True
}
}
return False
}
// ConvertToNative implements the ref.Val interface method.
func (l *baseList) ConvertToNative(typeDesc reflect.Type) (any, error) {
// If the underlying list value is assignable to the reflected type return it.
if reflect.TypeOf(l.value).AssignableTo(typeDesc) {
return l.value, nil
}
// If the list wrapper is assignable to the desired type return it.
if reflect.TypeOf(l).AssignableTo(typeDesc) {
return l, nil
}
// Attempt to convert the list to a set of well known protobuf types.
switch typeDesc {
case anyValueType:
json, err := l.ConvertToNative(jsonListValueType)
if err != nil {
return nil, err
}
return anypb.New(json.(proto.Message))
case jsonValueType, jsonListValueType:
jsonValues, err :=
l.ConvertToNative(reflect.TypeOf([]*structpb.Value{}))
if err != nil {
return nil, err
}
jsonList := &structpb.ListValue{Values: jsonValues.([]*structpb.Value)}
if typeDesc == jsonListValueType {
return jsonList, nil
}
return structpb.NewListValue(jsonList), nil
}
// Non-list conversion.
if typeDesc.Kind() != reflect.Slice && typeDesc.Kind() != reflect.Array {
return nil, fmt.Errorf("type conversion error from list to '%v'", typeDesc)
}
// List conversion.
// Allow the element ConvertToNative() function to determine whether conversion is possible.
otherElemType := typeDesc.Elem()
elemCount := l.size
var nativeList reflect.Value
if typeDesc.Kind() == reflect.Array {
nativeList = reflect.New(reflect.ArrayOf(elemCount, typeDesc)).Elem().Index(0)
} else {
nativeList = reflect.MakeSlice(typeDesc, elemCount, elemCount)
}
for i := 0; i < elemCount; i++ {
elem := l.NativeToValue(l.get(i))
nativeElemVal, err := elem.ConvertToNative(otherElemType)
if err != nil {
return nil, err
}
nativeList.Index(i).Set(reflect.ValueOf(nativeElemVal))
}
return nativeList.Interface(), nil
}
// ConvertToType implements the ref.Val interface method.
func (l *baseList) ConvertToType(typeVal ref.Type) ref.Val {
switch typeVal {
case ListType:
return l
case TypeType:
return ListType
}
return NewErr("type conversion error from '%s' to '%s'", ListType, typeVal)
}
// Equal implements the ref.Val interface method.
func (l *baseList) Equal(other ref.Val) ref.Val {
otherList, ok := other.(traits.Lister)
if !ok {
return False
}
if l.Size() != otherList.Size() {
return False
}
for i := IntZero; i < l.Size().(Int); i++ {
thisElem := l.Get(i)
otherElem := otherList.Get(i)
elemEq := Equal(thisElem, otherElem)
if elemEq == False {
return False
}
}
return True
}
// Get implements the traits.Indexer interface method.
func (l *baseList) Get(index ref.Val) ref.Val {
ind, err := IndexOrError(index)
if err != nil {
return ValOrErr(index, "%v", err)
}
if ind < 0 || ind >= l.size {
return NewErr("index '%d' out of range in list size '%d'", ind, l.Size())
}
return l.NativeToValue(l.get(ind))
}
// IsZeroValue returns true if the list is empty.
func (l *baseList) IsZeroValue() bool {
return l.size == 0
}
// Fold calls the FoldEntry method for each (index, value) pair in the list.
func (l *baseList) Fold(f traits.Folder) {
for i := 0; i < l.size; i++ {
if !f.FoldEntry(i, l.get(i)) {
break
}
}
}
// Iterator implements the traits.Iterable interface method.
func (l *baseList) Iterator() traits.Iterator {
return newListIterator(l)
}
// Size implements the traits.Sizer interface method.
func (l *baseList) Size() ref.Val {
return Int(l.size)
}
// Type implements the ref.Val interface method.
func (l *baseList) Type() ref.Type {
return ListType
}
// Value implements the ref.Val interface method.
func (l *baseList) Value() any {
return l.value
}
// String converts the list to a human readable string form.
func (l *baseList) String() string {
var sb strings.Builder
sb.WriteString("[")
for i := 0; i < l.size; i++ {
sb.WriteString(fmt.Sprintf("%v", l.get(i)))
if i != l.size-1 {
sb.WriteString(", ")
}
}
sb.WriteString("]")
return sb.String()
}
func formatList(l traits.Lister, sb *strings.Builder) {
sb.WriteString("[")
n, _ := l.Size().(Int)
for i := 0; i < int(n); i++ {
formatTo(sb, l.Get(Int(i)))
if i != int(n)-1 {
sb.WriteString(", ")
}
}
sb.WriteString("]")
}
func (l *baseList) format(sb *strings.Builder) {
formatList(l, sb)
}
// mutableList aggregates values into its internal storage. For use with internal CEL variables only.
type mutableList struct {
*baseList
mutableValues []ref.Val
}
// Add copies elements from the other list into the internal storage of the mutable list.
// The ref.Val returned by Add is the receiver.
func (l *mutableList) Add(other ref.Val) ref.Val {
switch otherList := other.(type) {
case *mutableList:
l.mutableValues = append(l.mutableValues, otherList.mutableValues...)
l.size += len(otherList.mutableValues)
case traits.Lister:
for i := IntZero; i < otherList.Size().(Int); i++ {
l.size++
l.mutableValues = append(l.mutableValues, otherList.Get(i))
}
default:
return MaybeNoSuchOverloadErr(otherList)
}
return l
}
// ToImmutableList returns an immutable list based on the internal storage of the mutable list.
func (l *mutableList) ToImmutableList() traits.Lister {
// The reference to internal state is guaranteed to be safe as this call is only performed
// when mutations have been completed.
return NewRefValList(l.Adapter, l.mutableValues)
}
// concatList combines two list implementations together into a view.
// The `Adapter` enables native type to CEL type conversions.
type concatList struct {
Adapter
value any
prevList traits.Lister
nextList traits.Lister
}
// Add implements the traits.Adder interface method.
func (l *concatList) Add(other ref.Val) ref.Val {
otherList, ok := other.(traits.Lister)
if !ok {
return MaybeNoSuchOverloadErr(other)
}
if l.Size() == IntZero {
return other
}
if otherList.Size() == IntZero {
return l
}
return &concatList{
Adapter: l.Adapter,
prevList: l,
nextList: otherList}
}
// Contains implements the traits.Container interface method.
func (l *concatList) Contains(elem ref.Val) ref.Val {
// The concat list relies on the IsErrorOrUnknown checks against the input element to be
// performed by the `prevList` and/or `nextList`.
prev := l.prevList.Contains(elem)
// Short-circuit the return if the elem was found in the prev list.
if prev == True {
return prev
}
// Return if the elem was found in the next list.
next := l.nextList.Contains(elem)
if next == True {
return next
}
// Handle the case where an error or unknown was encountered before checking next.
if IsUnknownOrError(prev) {
return prev
}
// Otherwise, rely on the next value as the representative result.
return next
}
// ConvertToNative implements the ref.Val interface method.
func (l *concatList) ConvertToNative(typeDesc reflect.Type) (any, error) {
combined := NewDynamicList(l.Adapter, l.Value().([]any))
return combined.ConvertToNative(typeDesc)
}
// ConvertToType implements the ref.Val interface method.
func (l *concatList) ConvertToType(typeVal ref.Type) ref.Val {
switch typeVal {
case ListType:
return l
case TypeType:
return ListType
}
return NewErr("type conversion error from '%s' to '%s'", ListType, typeVal)
}
// Equal implements the ref.Val interface method.
func (l *concatList) Equal(other ref.Val) ref.Val {
otherList, ok := other.(traits.Lister)
if !ok {
return False
}
if l.Size() != otherList.Size() {
return False
}
var maybeErr ref.Val
for i := IntZero; i < l.Size().(Int); i++ {
thisElem := l.Get(i)
otherElem := otherList.Get(i)
elemEq := Equal(thisElem, otherElem)
if elemEq == False {
return False
}
if maybeErr == nil && IsUnknownOrError(elemEq) {
maybeErr = elemEq
}
}
if maybeErr != nil {
return maybeErr
}
return True
}
// Get implements the traits.Indexer interface method.
func (l *concatList) Get(index ref.Val) ref.Val {
ind, err := IndexOrError(index)
if err != nil {
return ValOrErr(index, "%v", err)
}
i := Int(ind)
if i < l.prevList.Size().(Int) {
return l.prevList.Get(i)
}
offset := i - l.prevList.Size().(Int)
return l.nextList.Get(offset)
}
// IsZeroValue returns true if the list is empty.
func (l *concatList) IsZeroValue() bool {
return l.Size().(Int) == 0
}
// Fold calls the FoldEntry method for each (index, value) pair in the list.
func (l *concatList) Fold(f traits.Folder) {
for i := Int(0); i < l.Size().(Int); i++ {
if !f.FoldEntry(i, l.Get(i)) {
break
}
}
}
// Iterator implements the traits.Iterable interface method.
func (l *concatList) Iterator() traits.Iterator {
return newListIterator(l)
}
// Size implements the traits.Sizer interface method.
func (l *concatList) Size() ref.Val {
return l.prevList.Size().(Int).Add(l.nextList.Size())
}
// String converts the concatenated list to a human-readable string.
func (l *concatList) String() string {
var sb strings.Builder
sb.WriteString("[")
for i := Int(0); i < l.Size().(Int); i++ {
sb.WriteString(fmt.Sprintf("%v", l.Get(i)))
if i != l.Size().(Int)-1 {
sb.WriteString(", ")
}
}
sb.WriteString("]")
return sb.String()
}
// Type implements the ref.Val interface method.
func (l *concatList) Type() ref.Type {
return ListType
}
// Value implements the ref.Val interface method.
func (l *concatList) Value() any {
if l.value == nil {
merged := make([]any, l.Size().(Int))
prevLen := l.prevList.Size().(Int)
for i := Int(0); i < prevLen; i++ {
merged[i] = l.prevList.Get(i).Value()
}
nextLen := l.nextList.Size().(Int)
for j := Int(0); j < nextLen; j++ {
merged[prevLen+j] = l.nextList.Get(j).Value()
}
l.value = merged
}
return l.value
}
func newListIterator(listValue traits.Lister) traits.Iterator {
return &listIterator{
listValue: listValue,
len: listValue.Size().(Int),
}
}
type listIterator struct {
*baseIterator
listValue traits.Lister
cursor Int
len Int
}
// HasNext implements the traits.Iterator interface method.
func (it *listIterator) HasNext() ref.Val {
return Bool(it.cursor < it.len)
}
// Next implements the traits.Iterator interface method.
func (it *listIterator) Next() ref.Val {
if it.HasNext() == True {
index := it.cursor
it.cursor++
return it.listValue.Get(index)
}
return nil
}
// IndexOrError converts an input index value into either a lossless integer index or an error.
func IndexOrError(index ref.Val) (int, error) {
switch iv := index.(type) {
case Int:
return int(iv), nil
case Double:
if ik, ok := doubleToInt64Lossless(float64(iv)); ok {
return int(ik), nil
}
return -1, fmt.Errorf("unsupported index value %v in list", index)
case Uint:
if ik, ok := uint64ToInt64Lossless(uint64(iv)); ok {
return int(ik), nil
}
return -1, fmt.Errorf("unsupported index value %v in list", index)
default:
return -1, fmt.Errorf("unsupported index type '%s' in list", index.Type())
}
}
// ToFoldableList will create a Foldable version of a list suitable for key-value pair iteration.
//
// For values which are already Foldable, this call is a no-op. For all other values, the fold is
// driven via the Size() and Get() calls which means that the folding will function, but take a
// performance hit.
func ToFoldableList(l traits.Lister) traits.Foldable {
if f, ok := l.(traits.Foldable); ok {
return f
}
return interopFoldableList{Lister: l}
}
type interopFoldableList struct {
traits.Lister
}
// Fold implements the traits.Foldable interface method and performs an iteration over the
// range of elements of the list.
func (l interopFoldableList) Fold(f traits.Folder) {
sz := l.Size().(Int)
for i := Int(0); i < sz; i++ {
if !f.FoldEntry(i, l.Get(i)) {
break
}
}
}
// Copyright 2018 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package types
import (
"fmt"
"reflect"
"sort"
"strings"
"github.com/stoewer/go-strcase"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/reflect/protoreflect"
"github.com/google/cel-go/common/types/pb"
"github.com/google/cel-go/common/types/ref"
"github.com/google/cel-go/common/types/traits"
anypb "google.golang.org/protobuf/types/known/anypb"
structpb "google.golang.org/protobuf/types/known/structpb"
)
// NewDynamicMap returns a traits.Mapper value with dynamic key, value pairs.
func NewDynamicMap(adapter Adapter, value any) traits.Mapper {
refValue := reflect.ValueOf(value)
return &baseMap{
Adapter: adapter,
mapAccessor: newReflectMapAccessor(adapter, refValue),
value: value,
size: refValue.Len(),
}
}
// NewJSONStruct creates a traits.Mapper implementation backed by a JSON struct that has been
// encoded in protocol buffer form.
//
// The `adapter` argument provides type adaptation capabilities from proto to CEL.
func NewJSONStruct(adapter Adapter, value *structpb.Struct) traits.Mapper {
fields := value.GetFields()
return &baseMap{
Adapter: adapter,
mapAccessor: newJSONStructAccessor(adapter, fields),
value: value,
size: len(fields),
}
}
// NewRefValMap returns a specialized traits.Mapper with CEL valued keys and values.
func NewRefValMap(adapter Adapter, value map[ref.Val]ref.Val) traits.Mapper {
return &baseMap{
Adapter: adapter,
mapAccessor: newRefValMapAccessor(value),
value: value,
size: len(value),
}
}
// NewStringInterfaceMap returns a specialized traits.Mapper with string keys and interface values.
func NewStringInterfaceMap(adapter Adapter, value map[string]any) traits.Mapper {
return &baseMap{
Adapter: adapter,
mapAccessor: newStringIfaceMapAccessor(adapter, value),
value: value,
size: len(value),
}
}
// NewStringStringMap returns a specialized traits.Mapper with string keys and values.
func NewStringStringMap(adapter Adapter, value map[string]string) traits.Mapper {
return &baseMap{
Adapter: adapter,
mapAccessor: newStringMapAccessor(value),
value: value,
size: len(value),
}
}
// NewProtoMap returns a specialized traits.Mapper for handling protobuf map values.
func NewProtoMap(adapter Adapter, value *pb.Map) traits.Mapper {
return &protoMap{
Adapter: adapter,
value: value,
}
}
// NewMutableMap constructs a mutable map from an adapter and a set of map values.
func NewMutableMap(adapter Adapter, mutableValues map[ref.Val]ref.Val) traits.MutableMapper {
mutableCopy := make(map[ref.Val]ref.Val, len(mutableValues))
for k, v := range mutableValues {
mutableCopy[k] = v
}
m := &mutableMap{
baseMap: &baseMap{
Adapter: adapter,
mapAccessor: newRefValMapAccessor(mutableCopy),
value: mutableCopy,
size: len(mutableCopy),
},
mutableValues: mutableCopy,
}
return m
}
// mapAccessor is a private interface for finding values within a map and iterating over the keys.
// This interface implements portions of the API surface area required by the traits.Mapper
// interface.
type mapAccessor interface {
// Find returns a value, if one exists, for the input key.
//
// If the key is not found the function returns (nil, false).
Find(ref.Val) (ref.Val, bool)
// Iterator returns an Iterator over the map key set.
Iterator() traits.Iterator
// Fold calls the FoldEntry method for each (key, value) pair in the map.
Fold(traits.Folder)
}
// baseMap is a reflection based map implementation designed to handle a variety of map-like types.
//
// Since CEL is side-effect free, the base map represents an immutable object.
type baseMap struct {
// TypeAdapter used to convert keys and values accessed within the map.
Adapter
// mapAccessor interface implementation used to find and iterate over map keys.
mapAccessor
// value is the native Go value upon which the map type operators.
value any
// size is the number of entries in the map.
size int
}
// Contains implements the traits.Container interface method.
func (m *baseMap) Contains(index ref.Val) ref.Val {
_, found := m.Find(index)
return Bool(found)
}
// ConvertToNative implements the ref.Val interface method.
func (m *baseMap) ConvertToNative(typeDesc reflect.Type) (any, error) {
// If the map is already assignable to the desired type return it, e.g. interfaces and
// maps with the same key value types.
if reflect.TypeOf(m.value).AssignableTo(typeDesc) {
return m.value, nil
}
if reflect.TypeOf(m).AssignableTo(typeDesc) {
return m, nil
}
switch typeDesc {
case anyValueType:
json, err := m.ConvertToNative(jsonStructType)
if err != nil {
return nil, err
}
return anypb.New(json.(proto.Message))
case jsonValueType, jsonStructType:
jsonEntries, err :=
m.ConvertToNative(reflect.TypeOf(map[string]*structpb.Value{}))
if err != nil {
return nil, err
}
jsonMap := &structpb.Struct{Fields: jsonEntries.(map[string]*structpb.Value)}
if typeDesc == jsonStructType {
return jsonMap, nil
}
return structpb.NewStructValue(jsonMap), nil
}
// Unwrap pointers, but track their use.
isPtr := false
if typeDesc.Kind() == reflect.Ptr {
tk := typeDesc
typeDesc = typeDesc.Elem()
if typeDesc.Kind() == reflect.Ptr {
return nil, fmt.Errorf("unsupported type conversion to '%v'", tk)
}
isPtr = true
}
switch typeDesc.Kind() {
// Map conversion.
case reflect.Map:
otherKey := typeDesc.Key()
otherElem := typeDesc.Elem()
nativeMap := reflect.MakeMapWithSize(typeDesc, m.size)
it := m.Iterator()
for it.HasNext() == True {
key := it.Next()
refKeyValue, err := key.ConvertToNative(otherKey)
if err != nil {
return nil, err
}
refElemValue, err := m.Get(key).ConvertToNative(otherElem)
if err != nil {
return nil, err
}
nativeMap.SetMapIndex(reflect.ValueOf(refKeyValue), reflect.ValueOf(refElemValue))
}
return nativeMap.Interface(), nil
case reflect.Struct:
nativeStructPtr := reflect.New(typeDesc)
nativeStruct := nativeStructPtr.Elem()
it := m.Iterator()
for it.HasNext() == True {
key := it.Next()
// Ensure the field name being referenced is exported.
// Only exported (public) field names can be set by reflection, where the name
// must be at least one character in length and start with an upper-case letter.
fieldName := key.ConvertToType(StringType)
if IsError(fieldName) {
return nil, fieldName.(*Err)
}
name := string(fieldName.(String))
name = strcase.UpperCamelCase(name)
fieldRef := nativeStruct.FieldByName(name)
if !fieldRef.IsValid() {
return nil, fmt.Errorf("type conversion error, no such field '%s' in type '%v'", name, typeDesc)
}
fieldValue, err := m.Get(key).ConvertToNative(fieldRef.Type())
if err != nil {
return nil, err
}
fieldRef.Set(reflect.ValueOf(fieldValue))
}
if isPtr {
return nativeStructPtr.Interface(), nil
}
return nativeStruct.Interface(), nil
}
return nil, fmt.Errorf("type conversion error from map to '%v'", typeDesc)
}
// ConvertToType implements the ref.Val interface method.
func (m *baseMap) ConvertToType(typeVal ref.Type) ref.Val {
switch typeVal {
case MapType:
return m
case TypeType:
return MapType
}
return NewErr("type conversion error from '%s' to '%s'", MapType, typeVal)
}
// Equal implements the ref.Val interface method.
func (m *baseMap) Equal(other ref.Val) ref.Val {
otherMap, ok := other.(traits.Mapper)
if !ok {
return False
}
if m.Size() != otherMap.Size() {
return False
}
it := m.Iterator()
for it.HasNext() == True {
key := it.Next()
thisVal, _ := m.Find(key)
otherVal, found := otherMap.Find(key)
if !found {
return False
}
valEq := Equal(thisVal, otherVal)
if valEq == False {
return False
}
}
return True
}
// Get implements the traits.Indexer interface method.
func (m *baseMap) Get(key ref.Val) ref.Val {
v, found := m.Find(key)
if !found {
return ValOrErr(v, "no such key: %v", key)
}
return v
}
// IsZeroValue returns true if the map is empty.
func (m *baseMap) IsZeroValue() bool {
return m.size == 0
}
// Size implements the traits.Sizer interface method.
func (m *baseMap) Size() ref.Val {
return Int(m.size)
}
// String converts the map into a human-readable string.
func (m *baseMap) String() string {
var sb strings.Builder
sb.WriteString("{")
it := m.Iterator()
i := 0
for it.HasNext() == True {
k := it.Next()
v, _ := m.Find(k)
sb.WriteString(fmt.Sprintf("%v: %v", k, v))
if i != m.size-1 {
sb.WriteString(", ")
}
i++
}
sb.WriteString("}")
return sb.String()
}
type baseMapEntry struct {
key string
val string
}
func formatMap(m traits.Mapper, sb *strings.Builder) {
it := m.Iterator()
var ents []baseMapEntry
if s, ok := m.Size().(Int); ok {
ents = make([]baseMapEntry, 0, int(s))
}
for it.HasNext() == True {
k := it.Next()
v, _ := m.Find(k)
ents = append(ents, baseMapEntry{Format(k), Format(v)})
}
sort.SliceStable(ents, func(i, j int) bool {
return ents[i].key < ents[j].key
})
sb.WriteString("{")
for i, ent := range ents {
if i > 0 {
sb.WriteString(", ")
}
sb.WriteString(ent.key)
sb.WriteString(": ")
sb.WriteString(ent.val)
}
sb.WriteString("}")
}
func (m *baseMap) format(sb *strings.Builder) {
formatMap(m, sb)
}
// Type implements the ref.Val interface method.
func (m *baseMap) Type() ref.Type {
return MapType
}
// Value implements the ref.Val interface method.
func (m *baseMap) Value() any {
return m.value
}
// mutableMap holds onto a set of mutable values which are used for intermediate computations.
type mutableMap struct {
*baseMap
mutableValues map[ref.Val]ref.Val
}
// Insert implements the traits.MutableMapper interface method, returning true if the key insertion
// succeeds.
func (m *mutableMap) Insert(k, v ref.Val) ref.Val {
if _, found := m.Find(k); found {
return NewErr("insert failed: key %v already exists", k)
}
m.mutableValues[k] = v
return m
}
// ToImmutableMap implements the traits.MutableMapper interface method, converting a mutable map
// an immutable map implementation.
func (m *mutableMap) ToImmutableMap() traits.Mapper {
return NewRefValMap(m.Adapter, m.mutableValues)
}
func newJSONStructAccessor(adapter Adapter, st map[string]*structpb.Value) mapAccessor {
return &jsonStructAccessor{
Adapter: adapter,
st: st,
}
}
type jsonStructAccessor struct {
Adapter
st map[string]*structpb.Value
}
// Find searches the json struct field map for the input key value and returns (value, true) if
// found.
//
// If the key is not found the function returns (nil, false).
func (a *jsonStructAccessor) Find(key ref.Val) (ref.Val, bool) {
strKey, ok := key.(String)
if !ok {
return nil, false
}
keyVal, found := a.st[string(strKey)]
if !found {
return nil, false
}
return a.NativeToValue(keyVal), true
}
// Iterator creates a new traits.Iterator from the set of JSON struct field names.
func (a *jsonStructAccessor) Iterator() traits.Iterator {
// Copy the keys to make their order stable.
mapKeys := make([]string, len(a.st))
i := 0
for k := range a.st {
mapKeys[i] = k
i++
}
return &stringKeyIterator{
mapKeys: mapKeys,
len: len(mapKeys),
}
}
// Fold calls the FoldEntry method for each (key, value) pair in the map.
func (a *jsonStructAccessor) Fold(f traits.Folder) {
for k, v := range a.st {
if !f.FoldEntry(k, v) {
break
}
}
}
func newReflectMapAccessor(adapter Adapter, value reflect.Value) mapAccessor {
keyType := value.Type().Key()
return &reflectMapAccessor{
Adapter: adapter,
refValue: value,
keyType: keyType,
}
}
type reflectMapAccessor struct {
Adapter
refValue reflect.Value
keyType reflect.Type
}
// Find converts the input key to a native Golang type and then uses reflection to find the key,
// returning (value, true) if present.
//
// If the key is not found the function returns (nil, false).
func (m *reflectMapAccessor) Find(key ref.Val) (ref.Val, bool) {
if m.refValue.Len() == 0 {
return nil, false
}
if keyVal, found := m.findInternal(key); found {
return keyVal, true
}
switch k := key.(type) {
// Double is not a valid proto map key type, so check for the key as an int or uint.
case Double:
if ik, ok := doubleToInt64Lossless(float64(k)); ok {
if keyVal, found := m.findInternal(Int(ik)); found {
return keyVal, true
}
}
if uk, ok := doubleToUint64Lossless(float64(k)); ok {
return m.findInternal(Uint(uk))
}
// map keys of type double are not supported.
case Int:
if uk, ok := int64ToUint64Lossless(int64(k)); ok {
return m.findInternal(Uint(uk))
}
case Uint:
if ik, ok := uint64ToInt64Lossless(uint64(k)); ok {
return m.findInternal(Int(ik))
}
}
return nil, false
}
// findInternal attempts to convert the incoming key to the map's internal native type
// and then returns the value, if found.
func (m *reflectMapAccessor) findInternal(key ref.Val) (ref.Val, bool) {
k, err := key.ConvertToNative(m.keyType)
if err != nil {
return nil, false
}
refKey := reflect.ValueOf(k)
val := m.refValue.MapIndex(refKey)
if val.IsValid() {
return m.NativeToValue(val.Interface()), true
}
return nil, false
}
// Iterator creates a Golang reflection based traits.Iterator.
func (m *reflectMapAccessor) Iterator() traits.Iterator {
return &mapIterator{
Adapter: m.Adapter,
mapKeys: m.refValue.MapRange(),
len: m.refValue.Len(),
}
}
// Fold calls the FoldEntry method for each (key, value) pair in the map.
func (m *reflectMapAccessor) Fold(f traits.Folder) {
mapRange := m.refValue.MapRange()
for mapRange.Next() {
if !f.FoldEntry(mapRange.Key().Interface(), mapRange.Value().Interface()) {
break
}
}
}
func newRefValMapAccessor(mapVal map[ref.Val]ref.Val) mapAccessor {
return &refValMapAccessor{mapVal: mapVal}
}
type refValMapAccessor struct {
mapVal map[ref.Val]ref.Val
}
// Find uses native map accesses to find the key, returning (value, true) if present.
//
// If the key is not found the function returns (nil, false).
func (a *refValMapAccessor) Find(key ref.Val) (ref.Val, bool) {
if len(a.mapVal) == 0 {
return nil, false
}
if keyVal, found := a.mapVal[key]; found {
return keyVal, true
}
switch k := key.(type) {
case Double:
if ik, ok := doubleToInt64Lossless(float64(k)); ok {
if keyVal, found := a.mapVal[Int(ik)]; found {
return keyVal, found
}
}
if uk, ok := doubleToUint64Lossless(float64(k)); ok {
keyVal, found := a.mapVal[Uint(uk)]
return keyVal, found
}
// map keys of type double are not supported.
case Int:
if uk, ok := int64ToUint64Lossless(int64(k)); ok {
keyVal, found := a.mapVal[Uint(uk)]
return keyVal, found
}
case Uint:
if ik, ok := uint64ToInt64Lossless(uint64(k)); ok {
keyVal, found := a.mapVal[Int(ik)]
return keyVal, found
}
}
return nil, false
}
// Iterator produces a new traits.Iterator which iterates over the map keys via Golang reflection.
func (a *refValMapAccessor) Iterator() traits.Iterator {
return &mapIterator{
Adapter: DefaultTypeAdapter,
mapKeys: reflect.ValueOf(a.mapVal).MapRange(),
len: len(a.mapVal),
}
}
// Fold calls the FoldEntry method for each (key, value) pair in the map.
func (a *refValMapAccessor) Fold(f traits.Folder) {
for k, v := range a.mapVal {
if !f.FoldEntry(k, v) {
break
}
}
}
func newStringMapAccessor(strMap map[string]string) mapAccessor {
return &stringMapAccessor{mapVal: strMap}
}
type stringMapAccessor struct {
mapVal map[string]string
}
// Find uses native map accesses to find the key, returning (value, true) if present.
//
// If the key is not found the function returns (nil, false).
func (a *stringMapAccessor) Find(key ref.Val) (ref.Val, bool) {
strKey, ok := key.(String)
if !ok {
return nil, false
}
keyVal, found := a.mapVal[string(strKey)]
if !found {
return nil, false
}
return String(keyVal), true
}
// Iterator creates a new traits.Iterator from the string key set of the map.
func (a *stringMapAccessor) Iterator() traits.Iterator {
// Copy the keys to make their order stable.
mapKeys := make([]string, len(a.mapVal))
i := 0
for k := range a.mapVal {
mapKeys[i] = k
i++
}
return &stringKeyIterator{
mapKeys: mapKeys,
len: len(mapKeys),
}
}
// Fold calls the FoldEntry method for each (key, value) pair in the map.
func (a *stringMapAccessor) Fold(f traits.Folder) {
for k, v := range a.mapVal {
if !f.FoldEntry(k, v) {
break
}
}
}
func newStringIfaceMapAccessor(adapter Adapter, mapVal map[string]any) mapAccessor {
return &stringIfaceMapAccessor{
Adapter: adapter,
mapVal: mapVal,
}
}
type stringIfaceMapAccessor struct {
Adapter
mapVal map[string]any
}
// Find uses native map accesses to find the key, returning (value, true) if present.
//
// If the key is not found the function returns (nil, false).
func (a *stringIfaceMapAccessor) Find(key ref.Val) (ref.Val, bool) {
strKey, ok := key.(String)
if !ok {
return nil, false
}
keyVal, found := a.mapVal[string(strKey)]
if !found {
return nil, false
}
return a.NativeToValue(keyVal), true
}
// Iterator creates a new traits.Iterator from the string key set of the map.
func (a *stringIfaceMapAccessor) Iterator() traits.Iterator {
// Copy the keys to make their order stable.
mapKeys := make([]string, len(a.mapVal))
i := 0
for k := range a.mapVal {
mapKeys[i] = k
i++
}
return &stringKeyIterator{
mapKeys: mapKeys,
len: len(mapKeys),
}
}
// Fold calls the FoldEntry method for each (key, value) pair in the map.
func (a *stringIfaceMapAccessor) Fold(f traits.Folder) {
for k, v := range a.mapVal {
if !f.FoldEntry(k, v) {
break
}
}
}
// protoMap is a specialized, separate implementation of the traits.Mapper interfaces tailored to
// accessing protoreflect.Map values.
type protoMap struct {
Adapter
value *pb.Map
}
// Contains returns whether the map contains the given key.
func (m *protoMap) Contains(key ref.Val) ref.Val {
_, found := m.Find(key)
return Bool(found)
}
// ConvertToNative implements the ref.Val interface method.
//
// Note, assignment to Golang struct types is not yet supported.
func (m *protoMap) ConvertToNative(typeDesc reflect.Type) (any, error) {
// If the map is already assignable to the desired type return it, e.g. interfaces and
// maps with the same key value types.
switch typeDesc {
case anyValueType:
json, err := m.ConvertToNative(jsonStructType)
if err != nil {
return nil, err
}
return anypb.New(json.(proto.Message))
case jsonValueType, jsonStructType:
jsonEntries, err :=
m.ConvertToNative(reflect.TypeOf(map[string]*structpb.Value{}))
if err != nil {
return nil, err
}
jsonMap := &structpb.Struct{
Fields: jsonEntries.(map[string]*structpb.Value)}
if typeDesc == jsonStructType {
return jsonMap, nil
}
return structpb.NewStructValue(jsonMap), nil
}
switch typeDesc.Kind() {
case reflect.Struct, reflect.Ptr:
if reflect.TypeOf(m.value).AssignableTo(typeDesc) {
return m.value, nil
}
if reflect.TypeOf(m).AssignableTo(typeDesc) {
return m, nil
}
}
if typeDesc.Kind() != reflect.Map {
return nil, fmt.Errorf("unsupported type conversion: %v to map", typeDesc)
}
keyType := m.value.KeyType.ReflectType()
valType := m.value.ValueType.ReflectType()
otherKeyType := typeDesc.Key()
otherValType := typeDesc.Elem()
mapVal := reflect.MakeMapWithSize(typeDesc, m.value.Len())
var err error
m.value.Range(func(key protoreflect.MapKey, val protoreflect.Value) bool {
ntvKey := key.Interface()
ntvVal := val.Interface()
switch pv := ntvVal.(type) {
case protoreflect.Message:
ntvVal = pv.Interface()
}
if keyType == otherKeyType && valType == otherValType {
mapVal.SetMapIndex(reflect.ValueOf(ntvKey), reflect.ValueOf(ntvVal))
return true
}
celKey := m.NativeToValue(ntvKey)
celVal := m.NativeToValue(ntvVal)
ntvKey, err = celKey.ConvertToNative(otherKeyType)
if err != nil {
// early terminate the range loop.
return false
}
ntvVal, err = celVal.ConvertToNative(otherValType)
if err != nil {
// early terminate the range loop.
return false
}
mapVal.SetMapIndex(reflect.ValueOf(ntvKey), reflect.ValueOf(ntvVal))
return true
})
if err != nil {
return nil, err
}
return mapVal.Interface(), nil
}
// ConvertToType implements the ref.Val interface method.
func (m *protoMap) ConvertToType(typeVal ref.Type) ref.Val {
switch typeVal {
case MapType:
return m
case TypeType:
return MapType
}
return NewErr("type conversion error from '%s' to '%s'", MapType, typeVal)
}
// Equal implements the ref.Val interface method.
func (m *protoMap) Equal(other ref.Val) ref.Val {
otherMap, ok := other.(traits.Mapper)
if !ok {
return False
}
if m.value.Map.Len() != int(otherMap.Size().(Int)) {
return False
}
var retVal ref.Val = True
m.value.Range(func(key protoreflect.MapKey, val protoreflect.Value) bool {
keyVal := m.NativeToValue(key.Interface())
valVal := m.NativeToValue(val)
otherVal, found := otherMap.Find(keyVal)
if !found {
retVal = False
return false
}
valEq := Equal(valVal, otherVal)
if valEq != True {
retVal = valEq
return false
}
return true
})
return retVal
}
// Find returns whether the protoreflect.Map contains the input key.
//
// If the key is not found the function returns (nil, false).
func (m *protoMap) Find(key ref.Val) (ref.Val, bool) {
if keyVal, found := m.findInternal(key); found {
return keyVal, true
}
switch k := key.(type) {
// Double is not a valid proto map key type, so check for the key as an int or uint.
case Double:
if ik, ok := doubleToInt64Lossless(float64(k)); ok {
if keyVal, found := m.findInternal(Int(ik)); found {
return keyVal, true
}
}
if uk, ok := doubleToUint64Lossless(float64(k)); ok {
return m.findInternal(Uint(uk))
}
// map keys of type double are not supported.
case Int:
if uk, ok := int64ToUint64Lossless(int64(k)); ok {
return m.findInternal(Uint(uk))
}
case Uint:
if ik, ok := uint64ToInt64Lossless(uint64(k)); ok {
return m.findInternal(Int(ik))
}
}
return nil, false
}
// findInternal attempts to convert the incoming key to the map's internal native type
// and then returns the value, if found.
func (m *protoMap) findInternal(key ref.Val) (ref.Val, bool) {
// Convert the input key to the expected protobuf key type.
ntvKey, err := key.ConvertToNative(m.value.KeyType.ReflectType())
if err != nil {
return nil, false
}
// Use protoreflection to get the key value.
val := m.value.Get(protoreflect.ValueOf(ntvKey).MapKey())
if !val.IsValid() {
return nil, false
}
// Perform nominal type unwrapping from the input value.
switch v := val.Interface().(type) {
case protoreflect.List, protoreflect.Map:
// Maps do not support list or map values
return nil, false
default:
return m.NativeToValue(v), true
}
}
// Get implements the traits.Indexer interface method.
func (m *protoMap) Get(key ref.Val) ref.Val {
v, found := m.Find(key)
if !found {
return ValOrErr(v, "no such key: %v", key)
}
return v
}
// IsZeroValue returns true if the map is empty.
func (m *protoMap) IsZeroValue() bool {
return m.value.Len() == 0
}
// Iterator implements the traits.Iterable interface method.
func (m *protoMap) Iterator() traits.Iterator {
// Copy the keys to make their order stable.
mapKeys := make([]protoreflect.MapKey, 0, m.value.Len())
m.value.Range(func(k protoreflect.MapKey, v protoreflect.Value) bool {
mapKeys = append(mapKeys, k)
return true
})
return &protoMapIterator{
Adapter: m.Adapter,
mapKeys: mapKeys,
len: m.value.Len(),
}
}
// Fold calls the FoldEntry method for each (key, value) pair in the map.
func (m *protoMap) Fold(f traits.Folder) {
m.value.Range(func(k protoreflect.MapKey, v protoreflect.Value) bool {
return f.FoldEntry(k.Interface(), v.Interface())
})
}
// Size returns the number of entries in the protoreflect.Map.
func (m *protoMap) Size() ref.Val {
return Int(m.value.Len())
}
// Type implements the ref.Val interface method.
func (m *protoMap) Type() ref.Type {
return MapType
}
// Value implements the ref.Val interface method.
func (m *protoMap) Value() any {
return m.value
}
type mapIterator struct {
*baseIterator
Adapter
mapKeys *reflect.MapIter
cursor int
len int
}
// HasNext implements the traits.Iterator interface method.
func (it *mapIterator) HasNext() ref.Val {
return Bool(it.cursor < it.len)
}
// Next implements the traits.Iterator interface method.
func (it *mapIterator) Next() ref.Val {
if it.HasNext() == True && it.mapKeys.Next() {
it.cursor++
refKey := it.mapKeys.Key()
return it.NativeToValue(refKey.Interface())
}
return nil
}
type protoMapIterator struct {
*baseIterator
Adapter
mapKeys []protoreflect.MapKey
cursor int
len int
}
// HasNext implements the traits.Iterator interface method.
func (it *protoMapIterator) HasNext() ref.Val {
return Bool(it.cursor < it.len)
}
// Next implements the traits.Iterator interface method.
func (it *protoMapIterator) Next() ref.Val {
if it.HasNext() == True {
index := it.cursor
it.cursor++
refKey := it.mapKeys[index]
return it.NativeToValue(refKey.Interface())
}
return nil
}
type stringKeyIterator struct {
*baseIterator
mapKeys []string
cursor int
len int
}
// HasNext implements the traits.Iterator interface method.
func (it *stringKeyIterator) HasNext() ref.Val {
return Bool(it.cursor < it.len)
}
// Next implements the traits.Iterator interface method.
func (it *stringKeyIterator) Next() ref.Val {
if it.HasNext() == True {
index := it.cursor
it.cursor++
return String(it.mapKeys[index])
}
return nil
}
// ToFoldableMap will create a Foldable version of a map suitable for key-value pair iteration.
//
// For values which are already Foldable, this call is a no-op. For all other values, the fold
// is driven via the Iterator HasNext() and Next() calls as well as the map's Get() method
// which means that the folding will function, but take a performance hit.
func ToFoldableMap(m traits.Mapper) traits.Foldable {
if f, ok := m.(traits.Foldable); ok {
return f
}
return interopFoldableMap{Mapper: m}
}
type interopFoldableMap struct {
traits.Mapper
}
func (m interopFoldableMap) Fold(f traits.Folder) {
it := m.Iterator()
for it.HasNext() == True {
k := it.Next()
if !f.FoldEntry(k, m.Get(k)) {
break
}
}
}
// InsertMapKeyValue inserts a key, value pair into the target map if the target map does not
// already contain the given key.
//
// If the map is mutable, it is modified in-place per the MutableMapper contract.
// If the map is not mutable, a copy containing the new key, value pair is made.
func InsertMapKeyValue(m traits.Mapper, k, v ref.Val) ref.Val {
if mutable, ok := m.(traits.MutableMapper); ok {
return mutable.Insert(k, v)
}
// Otherwise perform the slow version of the insertion which makes a copy of the incoming map.
if _, found := m.Find(k); !found {
size := m.Size().(Int)
copy := make(map[ref.Val]ref.Val, size+1)
copy[k] = v
it := m.Iterator()
for it.HasNext() == True {
nextK := it.Next()
nextV := m.Get(nextK)
copy[nextK] = nextV
}
return DefaultTypeAdapter.NativeToValue(copy)
}
return NewErr("insert failed: key %v already exists", k)
}
// Copyright 2018 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package types
import (
"fmt"
"reflect"
"strings"
"google.golang.org/protobuf/proto"
"github.com/google/cel-go/common/types/ref"
anypb "google.golang.org/protobuf/types/known/anypb"
structpb "google.golang.org/protobuf/types/known/structpb"
)
// Null type implementation.
type Null structpb.NullValue
var (
// NullValue singleton.
NullValue = Null(structpb.NullValue_NULL_VALUE)
// golang reflect type for Null values.
nullReflectType = reflect.TypeOf(NullValue)
protoIfaceType = reflect.TypeOf((*proto.Message)(nil)).Elem()
)
// ConvertToNative implements ref.Val.ConvertToNative.
func (n Null) ConvertToNative(typeDesc reflect.Type) (any, error) {
switch typeDesc.Kind() {
case reflect.Int32:
switch typeDesc {
case jsonNullType:
return structpb.NullValue_NULL_VALUE, nil
case nullReflectType:
return n, nil
}
case reflect.Ptr:
switch typeDesc {
case anyValueType:
// Convert to a JSON-null before packing to an Any field since the enum value for JSON
// null cannot be packed directly.
pb, err := n.ConvertToNative(jsonValueType)
if err != nil {
return nil, err
}
return anypb.New(pb.(proto.Message))
case jsonValueType:
return structpb.NewNullValue(), nil
case boolWrapperType, byteWrapperType, doubleWrapperType, floatWrapperType,
int32WrapperType, int64WrapperType, stringWrapperType, uint32WrapperType,
uint64WrapperType, durationValueType, timestampValueType, protoIfaceType:
return nil, nil
case jsonListValueType, jsonStructType:
// skip handling
default:
if typeDesc.Implements(protoIfaceType) {
return nil, nil
}
}
case reflect.Interface:
nv := n.Value()
if reflect.TypeOf(nv).Implements(typeDesc) {
return nv, nil
}
if reflect.TypeOf(n).Implements(typeDesc) {
return n, nil
}
}
// If the type conversion isn't supported return an error.
return nil, fmt.Errorf("type conversion error from '%v' to '%v'", NullType, typeDesc)
}
// ConvertToType implements ref.Val.ConvertToType.
func (n Null) ConvertToType(typeVal ref.Type) ref.Val {
switch typeVal {
case StringType:
return String("null")
case NullType:
return n
case TypeType:
return NullType
}
return NewErr("type conversion error from '%s' to '%s'", NullType, typeVal)
}
// Equal implements ref.Val.Equal.
func (n Null) Equal(other ref.Val) ref.Val {
return Bool(NullType == other.Type())
}
// IsZeroValue returns true as null always represents an absent value.
func (n Null) IsZeroValue() bool {
return true
}
// Type implements ref.Val.Type.
func (n Null) Type() ref.Type {
return NullType
}
// Value implements ref.Val.Value.
func (n Null) Value() any {
return structpb.NullValue_NULL_VALUE
}
func (n Null) format(sb *strings.Builder) {
sb.WriteString("null")
}
// Copyright 2018 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package types
import (
"fmt"
"reflect"
"sort"
"strings"
"google.golang.org/protobuf/encoding/protojson"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/reflect/protoreflect"
"github.com/google/cel-go/common/types/pb"
"github.com/google/cel-go/common/types/ref"
anypb "google.golang.org/protobuf/types/known/anypb"
structpb "google.golang.org/protobuf/types/known/structpb"
)
type protoObj struct {
Adapter
value proto.Message
typeDesc *pb.TypeDescription
typeValue ref.Val
}
// NewObject returns an object based on a proto.Message value which handles
// conversion between protobuf type values and expression type values.
// Objects support indexing and iteration.
//
// Note: the type value is pulled from the list of registered types within the
// type provider. If the proto type is not registered within the type provider,
// then this will result in an error within the type adapter / provider.
func NewObject(adapter Adapter,
typeDesc *pb.TypeDescription,
typeValue ref.Val,
value proto.Message) ref.Val {
return &protoObj{
Adapter: adapter,
value: value,
typeDesc: typeDesc,
typeValue: typeValue}
}
func (o *protoObj) ConvertToNative(typeDesc reflect.Type) (any, error) {
srcPB := o.value
if reflect.TypeOf(srcPB).AssignableTo(typeDesc) {
return srcPB, nil
}
if reflect.TypeOf(o).AssignableTo(typeDesc) {
return o, nil
}
switch typeDesc {
case anyValueType:
_, isAny := srcPB.(*anypb.Any)
if isAny {
return srcPB, nil
}
return anypb.New(srcPB)
case jsonValueType:
// Marshal the proto to JSON first, and then rehydrate as protobuf.Value as there is no
// support for direct conversion from proto.Message to protobuf.Value.
bytes, err := protojson.Marshal(srcPB)
if err != nil {
return nil, err
}
json := &structpb.Value{}
err = protojson.Unmarshal(bytes, json)
if err != nil {
return nil, err
}
return json, nil
default:
if typeDesc == o.typeDesc.ReflectType() {
return o.value, nil
}
if typeDesc.Kind() == reflect.Ptr {
val := reflect.New(typeDesc.Elem()).Interface()
dstPB, ok := val.(proto.Message)
if ok {
err := pb.Merge(dstPB, srcPB)
if err != nil {
return nil, fmt.Errorf("type conversion error: %v", err)
}
return dstPB, nil
}
}
}
return nil, fmt.Errorf("type conversion error from '%T' to '%v'", o.value, typeDesc)
}
func (o *protoObj) ConvertToType(typeVal ref.Type) ref.Val {
switch typeVal {
default:
if o.Type().TypeName() == typeVal.TypeName() {
return o
}
case TypeType:
return o.typeValue
}
return NewErr("type conversion error from '%s' to '%s'", o.typeDesc.Name(), typeVal)
}
func (o *protoObj) Equal(other ref.Val) ref.Val {
otherPB, ok := other.Value().(proto.Message)
return Bool(ok && pb.Equal(o.value, otherPB))
}
// IsSet tests whether a field which is defined is set to a non-default value.
func (o *protoObj) IsSet(field ref.Val) ref.Val {
protoFieldName, ok := field.(String)
if !ok {
return MaybeNoSuchOverloadErr(field)
}
protoFieldStr := string(protoFieldName)
fd, found := o.typeDesc.FieldByName(protoFieldStr)
if !found {
return NewErr("no such field '%s'", field)
}
if fd.IsSet(o.value) {
return True
}
return False
}
// IsZeroValue returns true if the protobuf object is empty.
func (o *protoObj) IsZeroValue() bool {
return proto.Equal(o.value, o.typeDesc.Zero())
}
func (o *protoObj) Get(index ref.Val) ref.Val {
protoFieldName, ok := index.(String)
if !ok {
return MaybeNoSuchOverloadErr(index)
}
protoFieldStr := string(protoFieldName)
fd, found := o.typeDesc.FieldByName(protoFieldStr)
if !found {
return NewErr("no such field '%s'", index)
}
fv, err := fd.GetFrom(o.value)
if err != nil {
return NewErrFromString(err.Error())
}
return o.NativeToValue(fv)
}
func (o *protoObj) Type() ref.Type {
return o.typeValue.(ref.Type)
}
func (o *protoObj) Value() any {
return o.value
}
type protoObjField struct {
fd protoreflect.FieldDescriptor
v protoreflect.Value
}
func (o *protoObj) format(sb *strings.Builder) {
var fields []protoreflect.FieldDescriptor
o.value.ProtoReflect().Range(func(fd protoreflect.FieldDescriptor, v protoreflect.Value) bool {
fields = append(fields, fd)
return true
})
sort.SliceStable(fields, func(i, j int) bool {
return fields[i].Number() < fields[j].Number()
})
sb.WriteString(o.Type().TypeName())
sb.WriteString("{")
for i, field := range fields {
if i > 0 {
sb.WriteString(", ")
}
sb.WriteString(fmt.Sprintf("%s: ", field.Name()))
formatTo(sb, o.Get(String(field.Name())))
}
sb.WriteString("}")
}
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package types
import (
"errors"
"fmt"
"reflect"
"strings"
"github.com/google/cel-go/common/types/ref"
)
var (
// OptionalType indicates the runtime type of an optional value.
OptionalType = NewOpaqueType("optional_type")
// OptionalNone is a sentinel value which is used to indicate an empty optional value.
OptionalNone = &Optional{}
)
// OptionalOf returns an optional value which wraps a concrete CEL value.
func OptionalOf(value ref.Val) *Optional {
return &Optional{value: value}
}
// Optional value which points to a value if non-empty.
type Optional struct {
value ref.Val
}
// HasValue returns true if the optional has a value.
func (o *Optional) HasValue() bool {
return o.value != nil
}
// GetValue returns the wrapped value contained in the optional.
func (o *Optional) GetValue() ref.Val {
if !o.HasValue() {
return NewErr("optional.none() dereference")
}
return o.value
}
// ConvertToNative implements the ref.Val interface method.
func (o *Optional) ConvertToNative(typeDesc reflect.Type) (any, error) {
if !o.HasValue() {
return nil, errors.New("optional.none() dereference")
}
return o.value.ConvertToNative(typeDesc)
}
// ConvertToType implements the ref.Val interface method.
func (o *Optional) ConvertToType(typeVal ref.Type) ref.Val {
switch typeVal {
case OptionalType:
return o
case TypeType:
return OptionalType
}
return NewErr("type conversion error from '%s' to '%s'", OptionalType, typeVal)
}
// Equal determines whether the values contained by two optional values are equal.
func (o *Optional) Equal(other ref.Val) ref.Val {
otherOpt, isOpt := other.(*Optional)
if !isOpt {
return False
}
if !o.HasValue() {
return Bool(!otherOpt.HasValue())
}
if !otherOpt.HasValue() {
return False
}
return o.value.Equal(otherOpt.value)
}
func (o *Optional) String() string {
if o.HasValue() {
return fmt.Sprintf("optional(%v)", o.GetValue())
}
return "optional.none()"
}
func (o *Optional) format(sb *strings.Builder) {
if o.HasValue() {
sb.WriteString(`optional.of(`)
formatTo(sb, o.GetValue())
sb.WriteString(`)`)
} else {
sb.WriteString("optional.none()")
}
}
// Type implements the ref.Val interface method.
func (o *Optional) Type() ref.Type {
return OptionalType
}
// Value returns the underlying 'Value()' of the wrapped value, if present.
func (o *Optional) Value() any {
if o.value == nil {
return nil
}
return o.value.Value()
}
// Copyright 2021 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package types
import (
"math"
"time"
)
var (
doubleTwoTo64 = math.Ldexp(1.0, 64)
)
// addInt64Checked performs addition with overflow detection of two int64 values.
//
// If the operation fails the error return value will be non-nil.
func addInt64Checked(x, y int64) (int64, error) {
if (y > 0 && x > math.MaxInt64-y) || (y < 0 && x < math.MinInt64-y) {
return 0, errIntOverflow
}
return x + y, nil
}
// subtractInt64Checked performs subtraction with overflow detection of two int64 values.
//
// If the operation fails the error return value will be non-nil.
func subtractInt64Checked(x, y int64) (int64, error) {
if (y < 0 && x > math.MaxInt64+y) || (y > 0 && x < math.MinInt64+y) {
return 0, errIntOverflow
}
return x - y, nil
}
// negateInt64Checked performs negation with overflow detection of an int64.
//
// If the operation fails the error return value will be non-nil.
func negateInt64Checked(x int64) (int64, error) {
// In twos complement, negating MinInt64 would result in a valid of MaxInt64+1.
if x == math.MinInt64 {
return 0, errIntOverflow
}
return -x, nil
}
// multiplyInt64Checked performs multiplication with overflow detection of two int64 value.
//
// If the operation fails the error return value will be non-nil.
func multiplyInt64Checked(x, y int64) (int64, error) {
// Detecting multiplication overflow is more complicated than the others. The first two detect
// attempting to negate MinInt64, which would result in MaxInt64+1. The other four detect normal
// overflow conditions.
if (x == -1 && y == math.MinInt64) || (y == -1 && x == math.MinInt64) ||
// x is positive, y is positive
(x > 0 && y > 0 && x > math.MaxInt64/y) ||
// x is positive, y is negative
(x > 0 && y < 0 && y < math.MinInt64/x) ||
// x is negative, y is positive
(x < 0 && y > 0 && x < math.MinInt64/y) ||
// x is negative, y is negative
(x < 0 && y < 0 && y < math.MaxInt64/x) {
return 0, errIntOverflow
}
return x * y, nil
}
// divideInt64Checked performs division with overflow detection of two int64 values,
// as well as a division by zero check.
//
// If the operation fails the error return value will be non-nil.
func divideInt64Checked(x, y int64) (int64, error) {
// Division by zero.
if y == 0 {
return 0, errDivideByZero
}
// In twos complement, negating MinInt64 would result in a valid of MaxInt64+1.
if x == math.MinInt64 && y == -1 {
return 0, errIntOverflow
}
return x / y, nil
}
// moduloInt64Checked performs modulo with overflow detection of two int64 values
// as well as a modulus by zero check.
//
// If the operation fails the error return value will be non-nil.
func moduloInt64Checked(x, y int64) (int64, error) {
// Modulus by zero.
if y == 0 {
return 0, errModulusByZero
}
// In twos complement, negating MinInt64 would result in a valid of MaxInt64+1.
if x == math.MinInt64 && y == -1 {
return 0, errIntOverflow
}
return x % y, nil
}
// addUint64Checked performs addition with overflow detection of two uint64 values.
//
// If the operation fails due to overflow the error return value will be non-nil.
func addUint64Checked(x, y uint64) (uint64, error) {
if y > 0 && x > math.MaxUint64-y {
return 0, errUintOverflow
}
return x + y, nil
}
// subtractUint64Checked performs subtraction with overflow detection of two uint64 values.
//
// If the operation fails due to overflow the error return value will be non-nil.
func subtractUint64Checked(x, y uint64) (uint64, error) {
if y > x {
return 0, errUintOverflow
}
return x - y, nil
}
// multiplyUint64Checked performs multiplication with overflow detection of two uint64 values.
//
// If the operation fails due to overflow the error return value will be non-nil.
func multiplyUint64Checked(x, y uint64) (uint64, error) {
if y != 0 && x > math.MaxUint64/y {
return 0, errUintOverflow
}
return x * y, nil
}
// divideUint64Checked performs division with a test for division by zero.
//
// If the operation fails the error return value will be non-nil.
func divideUint64Checked(x, y uint64) (uint64, error) {
if y == 0 {
return 0, errDivideByZero
}
return x / y, nil
}
// moduloUint64Checked performs modulo with a test for modulus by zero.
//
// If the operation fails the error return value will be non-nil.
func moduloUint64Checked(x, y uint64) (uint64, error) {
if y == 0 {
return 0, errModulusByZero
}
return x % y, nil
}
// addDurationChecked performs addition with overflow detection of two time.Durations.
//
// If the operation fails due to overflow the error return value will be non-nil.
func addDurationChecked(x, y time.Duration) (time.Duration, error) {
val, err := addInt64Checked(int64(x), int64(y))
if err != nil {
return time.Duration(0), err
}
return time.Duration(val), nil
}
// subtractDurationChecked performs subtraction with overflow detection of two time.Durations.
//
// If the operation fails due to overflow the error return value will be non-nil.
func subtractDurationChecked(x, y time.Duration) (time.Duration, error) {
val, err := subtractInt64Checked(int64(x), int64(y))
if err != nil {
return time.Duration(0), err
}
return time.Duration(val), nil
}
// negateDurationChecked performs negation with overflow detection of a time.Duration.
//
// If the operation fails due to overflow the error return value will be non-nil.
func negateDurationChecked(x time.Duration) (time.Duration, error) {
val, err := negateInt64Checked(int64(x))
if err != nil {
return time.Duration(0), err
}
return time.Duration(val), nil
}
// addDurationChecked performs addition with overflow detection of a time.Time and time.Duration.
//
// If the operation fails due to overflow the error return value will be non-nil.
func addTimeDurationChecked(x time.Time, y time.Duration) (time.Time, error) {
// This is tricky. A time is represented as (int64, int32) where the first is seconds and second
// is nanoseconds. A duration is int64 representing nanoseconds. We cannot normalize time to int64
// as it could potentially overflow. The only way to proceed is to break time and duration into
// second and nanosecond components.
// First we break time into its components by truncating and subtracting.
sec1 := x.Truncate(time.Second).Unix() // Truncate to seconds.
nsec1 := x.Sub(x.Truncate(time.Second)).Nanoseconds() // Get nanoseconds by truncating and subtracting.
// Second we break duration into its components by dividing and modulo.
sec2 := int64(y) / int64(time.Second) // Truncate to seconds.
nsec2 := int64(y) % int64(time.Second) // Get remainder.
// Add seconds first, detecting any overflow.
sec, err := addInt64Checked(sec1, sec2)
if err != nil {
return time.Time{}, err
}
// Nanoseconds cannot overflow as time.Time normalizes them to [0, 999999999].
nsec := nsec1 + nsec2
// We need to normalize nanoseconds to be positive and carry extra nanoseconds to seconds.
// Adapted from time.Unix(int64, int64).
if nsec < 0 || nsec >= int64(time.Second) {
// Add seconds.
sec, err = addInt64Checked(sec, nsec/int64(time.Second))
if err != nil {
return time.Time{}, err
}
nsec -= (nsec / int64(time.Second)) * int64(time.Second)
if nsec < 0 {
// Subtract an extra second
sec, err = addInt64Checked(sec, -1)
if err != nil {
return time.Time{}, err
}
nsec += int64(time.Second)
}
}
// Check if the the number of seconds from Unix epoch is within our acceptable range.
if sec < minUnixTime || sec > maxUnixTime {
return time.Time{}, errTimestampOverflow
}
// Return resulting time and propagate time zone.
return time.Unix(sec, nsec).In(x.Location()), nil
}
// subtractTimeChecked performs subtraction with overflow detection of two time.Time.
//
// If the operation fails due to overflow the error return value will be non-nil.
func subtractTimeChecked(x, y time.Time) (time.Duration, error) {
// Similar to addTimeDurationOverflow() above.
// First we break time into its components by truncating and subtracting.
sec1 := x.Truncate(time.Second).Unix() // Truncate to seconds.
nsec1 := x.Sub(x.Truncate(time.Second)).Nanoseconds() // Get nanoseconds by truncating and subtracting.
// Second we break duration into its components by truncating and subtracting.
sec2 := y.Truncate(time.Second).Unix() // Truncate to seconds.
nsec2 := y.Sub(y.Truncate(time.Second)).Nanoseconds() // Get nanoseconds by truncating and subtracting.
// Subtract seconds first, detecting any overflow.
sec, err := subtractInt64Checked(sec1, sec2)
if err != nil {
return time.Duration(0), err
}
// Nanoseconds cannot overflow as time.Time normalizes them to [0, 999999999].
nsec := nsec1 - nsec2
// Scale seconds to nanoseconds detecting overflow.
tsec, err := multiplyInt64Checked(sec, int64(time.Second))
if err != nil {
return time.Duration(0), err
}
// Lastly we need to add the two nanoseconds together.
val, err := addInt64Checked(tsec, nsec)
if err != nil {
return time.Duration(0), err
}
return time.Duration(val), nil
}
// subtractTimeDurationChecked performs subtraction with overflow detection of a time.Time and
// time.Duration.
//
// If the operation fails due to overflow the error return value will be non-nil.
func subtractTimeDurationChecked(x time.Time, y time.Duration) (time.Time, error) {
// The easiest way to implement this is to negate y and add them.
// x - y = x + -y
val, err := negateDurationChecked(y)
if err != nil {
return time.Time{}, err
}
return addTimeDurationChecked(x, val)
}
// doubleToInt64Checked converts a double to an int64 value.
//
// If the conversion fails due to overflow the error return value will be non-nil.
func doubleToInt64Checked(v float64) (int64, error) {
if math.IsInf(v, 0) || math.IsNaN(v) || v <= float64(math.MinInt64) || v >= float64(math.MaxInt64) {
return 0, errIntOverflow
}
return int64(v), nil
}
// doubleToInt64Checked converts a double to a uint64 value.
//
// If the conversion fails due to overflow the error return value will be non-nil.
func doubleToUint64Checked(v float64) (uint64, error) {
if math.IsInf(v, 0) || math.IsNaN(v) || v < 0 || v >= doubleTwoTo64 {
return 0, errUintOverflow
}
return uint64(v), nil
}
// int64ToUint64Checked converts an int64 to a uint64 value.
//
// If the conversion fails due to overflow the error return value will be non-nil.
func int64ToUint64Checked(v int64) (uint64, error) {
if v < 0 {
return 0, errUintOverflow
}
return uint64(v), nil
}
// int64ToInt8Checked converts an int64 to an int8 value.
//
// If the conversion fails due to overflow the error return value will be non-nil.
func int64ToInt8Checked(v int64) (int8, error) {
if v < math.MinInt8 || v > math.MaxInt8 {
return 0, errIntOverflow
}
return int8(v), nil
}
// int64ToInt16Checked converts an int64 to an int16 value.
//
// If the conversion fails due to overflow the error return value will be non-nil.
func int64ToInt16Checked(v int64) (int16, error) {
if v < math.MinInt16 || v > math.MaxInt16 {
return 0, errIntOverflow
}
return int16(v), nil
}
// int64ToInt32Checked converts an int64 to an int32 value.
//
// If the conversion fails due to overflow the error return value will be non-nil.
func int64ToInt32Checked(v int64) (int32, error) {
if v < math.MinInt32 || v > math.MaxInt32 {
return 0, errIntOverflow
}
return int32(v), nil
}
// uint64ToUint8Checked converts a uint64 to a uint8 value.
//
// If the conversion fails due to overflow the error return value will be non-nil.
func uint64ToUint8Checked(v uint64) (uint8, error) {
if v > math.MaxUint8 {
return 0, errUintOverflow
}
return uint8(v), nil
}
// uint64ToUint16Checked converts a uint64 to a uint16 value.
//
// If the conversion fails due to overflow the error return value will be non-nil.
func uint64ToUint16Checked(v uint64) (uint16, error) {
if v > math.MaxUint16 {
return 0, errUintOverflow
}
return uint16(v), nil
}
// uint64ToUint32Checked converts a uint64 to a uint32 value.
//
// If the conversion fails due to overflow the error return value will be non-nil.
func uint64ToUint32Checked(v uint64) (uint32, error) {
if v > math.MaxUint32 {
return 0, errUintOverflow
}
return uint32(v), nil
}
// uint64ToInt64Checked converts a uint64 to an int64 value.
//
// If the conversion fails due to overflow the error return value will be non-nil.
func uint64ToInt64Checked(v uint64) (int64, error) {
if v > math.MaxInt64 {
return 0, errIntOverflow
}
return int64(v), nil
}
func doubleToUint64Lossless(v float64) (uint64, bool) {
u, err := doubleToUint64Checked(v)
if err != nil {
return 0, false
}
if float64(u) != v {
return 0, false
}
return u, true
}
func doubleToInt64Lossless(v float64) (int64, bool) {
i, err := doubleToInt64Checked(v)
if err != nil {
return 0, false
}
if float64(i) != v {
return 0, false
}
return i, true
}
func int64ToUint64Lossless(v int64) (uint64, bool) {
u, err := int64ToUint64Checked(v)
return u, err == nil
}
func uint64ToInt64Lossless(v uint64) (int64, bool) {
i, err := uint64ToInt64Checked(v)
return i, err == nil
}
// Copyright 2018 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package pb
import (
"google.golang.org/protobuf/reflect/protoreflect"
)
// newEnumValueDescription produces an enum value description with the fully qualified enum value
// name and the enum value descriptor.
func newEnumValueDescription(name string, desc protoreflect.EnumValueDescriptor) *EnumValueDescription {
return &EnumValueDescription{
enumValueName: name,
desc: desc,
}
}
// EnumValueDescription maps a fully-qualified enum value name to its numeric value.
type EnumValueDescription struct {
enumValueName string
desc protoreflect.EnumValueDescriptor
}
// Name returns the fully-qualified identifier name for the enum value.
func (ed *EnumValueDescription) Name() string {
return ed.enumValueName
}
// Value returns the (numeric) value of the enum.
func (ed *EnumValueDescription) Value() int32 {
return int32(ed.desc.Number())
}
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package pb
import (
"bytes"
"reflect"
"google.golang.org/protobuf/encoding/protowire"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/reflect/protoreflect"
anypb "google.golang.org/protobuf/types/known/anypb"
)
// Equal returns whether two proto.Message instances are equal using the following criteria:
//
// - Messages must share the same instance of the type descriptor
// - Known set fields are compared using semantics equality
// - Bytes are compared using bytes.Equal
// - Scalar values are compared with operator ==
// - List and map types are equal if they have the same length and all elements are equal
// - Messages are equal if they share the same descriptor and all set fields are equal
// - Unknown fields are compared using byte equality
// - NaN values are not equal to each other
// - google.protobuf.Any values are unpacked before comparison
// - If the type descriptor for a protobuf.Any cannot be found, byte equality is used rather than
// semantic equality.
//
// This method of proto equality mirrors the behavior of the C++ protobuf MessageDifferencer
// whereas the golang proto.Equal implementation mirrors the Java protobuf equals() methods
// behaviors which needed to treat NaN values as equal due to Java semantics.
func Equal(x, y proto.Message) bool {
if x == nil || y == nil {
return x == nil && y == nil
}
xRef := x.ProtoReflect()
yRef := y.ProtoReflect()
return equalMessage(xRef, yRef)
}
func equalMessage(mx, my protoreflect.Message) bool {
// Note, the original proto.Equal upon which this implementation is based does not specifically handle the
// case when both messages are invalid. It is assumed that the descriptors will be equal and that byte-wise
// comparison will be used, though the semantics of validity are neither clear, nor promised within the
// proto.Equal implementation.
if mx.IsValid() != my.IsValid() || mx.Descriptor() != my.Descriptor() {
return false
}
// This is an innovation on the default proto.Equal where protobuf.Any values are unpacked before comparison
// as otherwise the Any values are compared by bytes rather than structurally.
if isAny(mx) && isAny(my) {
ax := mx.Interface().(*anypb.Any)
ay := my.Interface().(*anypb.Any)
// If the values are not the same type url, return false.
if ax.GetTypeUrl() != ay.GetTypeUrl() {
return false
}
// If the values are byte equal, then return true.
if bytes.Equal(ax.GetValue(), ay.GetValue()) {
return true
}
// Otherwise fall through to the semantic comparison of the any values.
x, err := ax.UnmarshalNew()
if err != nil {
return false
}
y, err := ay.UnmarshalNew()
if err != nil {
return false
}
// Recursively compare the unwrapped messages to ensure nested Any values are unwrapped accordingly.
return equalMessage(x.ProtoReflect(), y.ProtoReflect())
}
// Walk the set fields to determine field-wise equality
nx := 0
equal := true
mx.Range(func(fd protoreflect.FieldDescriptor, vx protoreflect.Value) bool {
nx++
equal = my.Has(fd) && equalField(fd, vx, my.Get(fd))
return equal
})
if !equal {
return false
}
// Establish the count of set fields on message y
ny := 0
my.Range(func(protoreflect.FieldDescriptor, protoreflect.Value) bool {
ny++
return true
})
// If the number of set fields is not equal return false.
if nx != ny {
return false
}
return equalUnknown(mx.GetUnknown(), my.GetUnknown())
}
func equalField(fd protoreflect.FieldDescriptor, x, y protoreflect.Value) bool {
switch {
case fd.IsMap():
return equalMap(fd, x.Map(), y.Map())
case fd.IsList():
return equalList(fd, x.List(), y.List())
default:
return equalValue(fd, x, y)
}
}
func equalMap(fd protoreflect.FieldDescriptor, x, y protoreflect.Map) bool {
if x.Len() != y.Len() {
return false
}
equal := true
x.Range(func(k protoreflect.MapKey, vx protoreflect.Value) bool {
vy := y.Get(k)
equal = y.Has(k) && equalValue(fd.MapValue(), vx, vy)
return equal
})
return equal
}
func equalList(fd protoreflect.FieldDescriptor, x, y protoreflect.List) bool {
if x.Len() != y.Len() {
return false
}
for i := x.Len() - 1; i >= 0; i-- {
if !equalValue(fd, x.Get(i), y.Get(i)) {
return false
}
}
return true
}
func equalValue(fd protoreflect.FieldDescriptor, x, y protoreflect.Value) bool {
switch fd.Kind() {
case protoreflect.BoolKind:
return x.Bool() == y.Bool()
case protoreflect.EnumKind:
return x.Enum() == y.Enum()
case protoreflect.Int32Kind, protoreflect.Sint32Kind,
protoreflect.Int64Kind, protoreflect.Sint64Kind,
protoreflect.Sfixed32Kind, protoreflect.Sfixed64Kind:
return x.Int() == y.Int()
case protoreflect.Uint32Kind, protoreflect.Uint64Kind,
protoreflect.Fixed32Kind, protoreflect.Fixed64Kind:
return x.Uint() == y.Uint()
case protoreflect.FloatKind, protoreflect.DoubleKind:
return x.Float() == y.Float()
case protoreflect.StringKind:
return x.String() == y.String()
case protoreflect.BytesKind:
return bytes.Equal(x.Bytes(), y.Bytes())
case protoreflect.MessageKind, protoreflect.GroupKind:
return equalMessage(x.Message(), y.Message())
default:
return x.Interface() == y.Interface()
}
}
func equalUnknown(x, y protoreflect.RawFields) bool {
lenX := len(x)
lenY := len(y)
if lenX != lenY {
return false
}
if lenX == 0 {
return true
}
if bytes.Equal([]byte(x), []byte(y)) {
return true
}
mx := make(map[protoreflect.FieldNumber]protoreflect.RawFields)
my := make(map[protoreflect.FieldNumber]protoreflect.RawFields)
for len(x) > 0 {
fnum, _, n := protowire.ConsumeField(x)
mx[fnum] = append(mx[fnum], x[:n]...)
x = x[n:]
}
for len(y) > 0 {
fnum, _, n := protowire.ConsumeField(y)
my[fnum] = append(my[fnum], y[:n]...)
y = y[n:]
}
return reflect.DeepEqual(mx, my)
}
func isAny(m protoreflect.Message) bool {
return string(m.Descriptor().FullName()) == "google.protobuf.Any"
}
// Copyright 2018 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package pb
import (
"fmt"
"google.golang.org/protobuf/reflect/protoreflect"
dynamicpb "google.golang.org/protobuf/types/dynamicpb"
)
// newFileDescription returns a FileDescription instance with a complete listing of all the message
// types and enum values, as well as a map of extensions declared within any scope in the file.
func newFileDescription(fileDesc protoreflect.FileDescriptor, pbdb *Db) (*FileDescription, extensionMap) {
metadata := collectFileMetadata(fileDesc)
enums := make(map[string]*EnumValueDescription)
for name, enumVal := range metadata.enumValues {
enums[name] = newEnumValueDescription(name, enumVal)
}
types := make(map[string]*TypeDescription)
for name, msgType := range metadata.msgTypes {
types[name] = newTypeDescription(name, msgType, pbdb.extensions)
}
fileExtMap := make(extensionMap)
for typeName, extensions := range metadata.msgExtensionMap {
messageExtMap, found := fileExtMap[typeName]
if !found {
messageExtMap = make(map[string]*FieldDescription)
}
for _, ext := range extensions {
extDesc := dynamicpb.NewExtensionType(ext).TypeDescriptor()
messageExtMap[string(ext.FullName())] = newFieldDescription(extDesc)
}
fileExtMap[typeName] = messageExtMap
}
return &FileDescription{
name: fileDesc.Path(),
types: types,
enums: enums,
}, fileExtMap
}
// FileDescription holds a map of all types and enum values declared within a proto file.
type FileDescription struct {
name string
types map[string]*TypeDescription
enums map[string]*EnumValueDescription
}
// Copy creates a copy of the FileDescription with updated Db references within its types.
func (fd *FileDescription) Copy(pbdb *Db) *FileDescription {
typesCopy := make(map[string]*TypeDescription, len(fd.types))
for k, v := range fd.types {
typesCopy[k] = v.Copy(pbdb)
}
return &FileDescription{
name: fd.name,
types: typesCopy,
enums: fd.enums,
}
}
// GetName returns the fully qualified file path for the file.
func (fd *FileDescription) GetName() string {
return fd.name
}
// GetEnumDescription returns an EnumDescription for a qualified enum value
// name declared within the .proto file.
func (fd *FileDescription) GetEnumDescription(enumName string) (*EnumValueDescription, bool) {
ed, found := fd.enums[sanitizeProtoName(enumName)]
return ed, found
}
// GetEnumNames returns the string names of all enum values in the file.
func (fd *FileDescription) GetEnumNames() []string {
enumNames := make([]string, len(fd.enums))
i := 0
for _, e := range fd.enums {
enumNames[i] = e.Name()
i++
}
return enumNames
}
// GetTypeDescription returns a TypeDescription for a qualified protobuf message type name
// declared within the .proto file.
func (fd *FileDescription) GetTypeDescription(typeName string) (*TypeDescription, bool) {
td, found := fd.types[sanitizeProtoName(typeName)]
return td, found
}
// GetTypeNames returns the list of all type names contained within the file.
func (fd *FileDescription) GetTypeNames() []string {
typeNames := make([]string, len(fd.types))
i := 0
for _, t := range fd.types {
typeNames[i] = t.Name()
i++
}
return typeNames
}
// sanitizeProtoName strips the leading '.' from the proto message name.
func sanitizeProtoName(name string) string {
if name != "" && name[0] == '.' {
return name[1:]
}
return name
}
// fileMetadata is a flattened view of message types and enum values within a file descriptor.
type fileMetadata struct {
// msgTypes maps from fully-qualified message name to descriptor.
msgTypes map[string]protoreflect.MessageDescriptor
// enumValues maps from fully-qualified enum value to enum value descriptor.
enumValues map[string]protoreflect.EnumValueDescriptor
// msgExtensionMap maps from the protobuf message name being extended to a set of extensions
// for the type.
msgExtensionMap map[string][]protoreflect.ExtensionDescriptor
// TODO: support enum type definitions for use in future type-check enhancements.
}
// collectFileMetadata traverses the proto file object graph to collect message types and enum
// values and index them by their fully qualified names.
func collectFileMetadata(fileDesc protoreflect.FileDescriptor) *fileMetadata {
msgTypes := make(map[string]protoreflect.MessageDescriptor)
enumValues := make(map[string]protoreflect.EnumValueDescriptor)
msgExtensionMap := make(map[string][]protoreflect.ExtensionDescriptor)
collectMsgTypes(fileDesc.Messages(), msgTypes, enumValues, msgExtensionMap)
collectEnumValues(fileDesc.Enums(), enumValues)
collectExtensions(fileDesc.Extensions(), msgExtensionMap)
return &fileMetadata{
msgTypes: msgTypes,
enumValues: enumValues,
msgExtensionMap: msgExtensionMap,
}
}
// collectMsgTypes recursively collects messages, nested messages, and nested enums into a map of
// fully qualified protobuf names to descriptors.
func collectMsgTypes(msgTypes protoreflect.MessageDescriptors,
msgTypeMap map[string]protoreflect.MessageDescriptor,
enumValueMap map[string]protoreflect.EnumValueDescriptor,
msgExtensionMap map[string][]protoreflect.ExtensionDescriptor) {
for i := 0; i < msgTypes.Len(); i++ {
msgType := msgTypes.Get(i)
msgTypeMap[string(msgType.FullName())] = msgType
nestedMsgTypes := msgType.Messages()
if nestedMsgTypes.Len() != 0 {
collectMsgTypes(nestedMsgTypes, msgTypeMap, enumValueMap, msgExtensionMap)
}
nestedEnumTypes := msgType.Enums()
if nestedEnumTypes.Len() != 0 {
collectEnumValues(nestedEnumTypes, enumValueMap)
}
nestedExtensions := msgType.Extensions()
if nestedExtensions.Len() != 0 {
collectExtensions(nestedExtensions, msgExtensionMap)
}
}
}
// collectEnumValues accumulates the enum values within an enum declaration.
func collectEnumValues(enumTypes protoreflect.EnumDescriptors, enumValueMap map[string]protoreflect.EnumValueDescriptor) {
for i := 0; i < enumTypes.Len(); i++ {
enumType := enumTypes.Get(i)
enumTypeValues := enumType.Values()
for j := 0; j < enumTypeValues.Len(); j++ {
enumValue := enumTypeValues.Get(j)
enumValueName := fmt.Sprintf("%s.%s", string(enumType.FullName()), string(enumValue.Name()))
enumValueMap[enumValueName] = enumValue
}
}
}
func collectExtensions(extensions protoreflect.ExtensionDescriptors, msgExtensionMap map[string][]protoreflect.ExtensionDescriptor) {
for i := 0; i < extensions.Len(); i++ {
ext := extensions.Get(i)
extendsMsg := string(ext.ContainingMessage().FullName())
msgExts, found := msgExtensionMap[extendsMsg]
if !found {
msgExts = []protoreflect.ExtensionDescriptor{}
}
msgExts = append(msgExts, ext)
msgExtensionMap[extendsMsg] = msgExts
}
}
// Copyright 2018 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Package pb reflects over protocol buffer descriptors to generate objects
// that simplify type, enum, and field lookup.
package pb
import (
"fmt"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/reflect/protoreflect"
"google.golang.org/protobuf/reflect/protoregistry"
anypb "google.golang.org/protobuf/types/known/anypb"
durpb "google.golang.org/protobuf/types/known/durationpb"
emptypb "google.golang.org/protobuf/types/known/emptypb"
structpb "google.golang.org/protobuf/types/known/structpb"
tspb "google.golang.org/protobuf/types/known/timestamppb"
wrapperspb "google.golang.org/protobuf/types/known/wrapperspb"
)
// Db maps from file / message / enum name to file description.
//
// Each Db is isolated from each other, and while information about protobuf descriptors may be
// fetched from the global protobuf registry, no descriptors are added to this registry, else
// the isolation guarantees of the Db object would be violated.
type Db struct {
revFileDescriptorMap map[string]*FileDescription
// files contains the deduped set of FileDescriptions whose types are contained in the pb.Db.
files []*FileDescription
// extensions contains the mapping between a given type name, extension name and its FieldDescription
extensions map[string]map[string]*FieldDescription
}
// extensionsMap is a type alias to a map[typeName]map[extensionName]*FieldDescription
type extensionMap = map[string]map[string]*FieldDescription
var (
// DefaultDb used at evaluation time or unless overridden at check time.
DefaultDb = &Db{
revFileDescriptorMap: make(map[string]*FileDescription),
files: []*FileDescription{},
extensions: make(extensionMap),
}
)
// Merge will copy the source proto message into the destination, or error if the merge cannot be completed.
//
// Unlike the proto.Merge, this method will fallback to proto.Marshal/Unmarshal of the two proto messages do not
// share the same instance of their type descriptor.
func Merge(dstPB, srcPB proto.Message) error {
src, dst := srcPB.ProtoReflect(), dstPB.ProtoReflect()
if src.Descriptor() == dst.Descriptor() {
proto.Merge(dstPB, srcPB)
return nil
}
if src.Descriptor().FullName() != dst.Descriptor().FullName() {
return fmt.Errorf("pb.Merge() arguments must be the same type. got: %v, %v",
dst.Descriptor().FullName(), src.Descriptor().FullName())
}
bytes, err := proto.Marshal(srcPB)
if err != nil {
return fmt.Errorf("pb.Merge(dstPB, srcPB) failed to marshal source proto: %v", err)
}
err = proto.Unmarshal(bytes, dstPB)
if err != nil {
return fmt.Errorf("pb.Merge(dstPB, srcPB) failed to unmarshal to dest proto: %v", err)
}
return nil
}
// NewDb creates a new `pb.Db` with an empty type name to file description map.
func NewDb() *Db {
pbdb := &Db{
revFileDescriptorMap: make(map[string]*FileDescription),
files: []*FileDescription{},
extensions: make(extensionMap),
}
// The FileDescription objects in the default db contain lazily initialized TypeDescription
// values which may point to the state contained in the DefaultDb irrespective of this shallow
// copy; however, the type graph for a field is idempotently computed, and is guaranteed to
// only be initialized once thanks to atomic values within the TypeDescription objects, so it
// is safe to share these values across instances.
for k, v := range DefaultDb.revFileDescriptorMap {
pbdb.revFileDescriptorMap[k] = v
}
pbdb.files = append(pbdb.files, DefaultDb.files...)
return pbdb
}
// Copy creates a copy of the current database with its own internal descriptor mapping.
func (pbdb *Db) Copy() *Db {
copy := NewDb()
for _, fd := range pbdb.files {
hasFile := false
for _, fd2 := range copy.files {
if fd2 == fd {
hasFile = true
}
}
if !hasFile {
fd = fd.Copy(copy)
copy.files = append(copy.files, fd)
}
for _, enumValName := range fd.GetEnumNames() {
copy.revFileDescriptorMap[enumValName] = fd
}
for _, msgTypeName := range fd.GetTypeNames() {
copy.revFileDescriptorMap[msgTypeName] = fd
}
copy.revFileDescriptorMap[fd.GetName()] = fd
}
for typeName, extFieldMap := range pbdb.extensions {
copyExtFieldMap, found := copy.extensions[typeName]
if !found {
copyExtFieldMap = make(map[string]*FieldDescription, len(extFieldMap))
}
for extFieldName, fd := range extFieldMap {
copyExtFieldMap[extFieldName] = fd
}
copy.extensions[typeName] = copyExtFieldMap
}
return copy
}
// FileDescriptions returns the set of file descriptions associated with this db.
func (pbdb *Db) FileDescriptions() []*FileDescription {
return pbdb.files
}
// RegisterDescriptor produces a `FileDescription` from a `FileDescriptor` and registers the
// message and enum types into the `pb.Db`.
func (pbdb *Db) RegisterDescriptor(fileDesc protoreflect.FileDescriptor) (*FileDescription, error) {
fd, found := pbdb.revFileDescriptorMap[fileDesc.Path()]
if found {
return fd, nil
}
// Make sure to search the global registry to see if a protoreflect.FileDescriptor for
// the file specified has been linked into the binary. If so, use the copy of the descriptor
// from the global cache.
//
// Note: Proto reflection relies on descriptor values being object equal rather than object
// equivalence. This choice means that a FieldDescriptor generated from a FileDescriptorProto
// will be incompatible with the FieldDescriptor in the global registry and any message created
// from that global registry.
globalFD, err := protoregistry.GlobalFiles.FindFileByPath(fileDesc.Path())
if err == nil {
fileDesc = globalFD
}
var fileExtMap extensionMap
fd, fileExtMap = newFileDescription(fileDesc, pbdb)
for _, enumValName := range fd.GetEnumNames() {
pbdb.revFileDescriptorMap[enumValName] = fd
}
for _, msgTypeName := range fd.GetTypeNames() {
pbdb.revFileDescriptorMap[msgTypeName] = fd
}
pbdb.revFileDescriptorMap[fd.GetName()] = fd
// Return the specific file descriptor registered.
pbdb.files = append(pbdb.files, fd)
// Index the protobuf message extensions from the file into the pbdb
for typeName, extMap := range fileExtMap {
typeExtMap, found := pbdb.extensions[typeName]
if !found {
pbdb.extensions[typeName] = extMap
continue
}
for extName, field := range extMap {
typeExtMap[extName] = field
}
}
return fd, nil
}
// RegisterMessage produces a `FileDescription` from a `message` and registers the message and all
// other definitions within the message file into the `pb.Db`.
func (pbdb *Db) RegisterMessage(message proto.Message) (*FileDescription, error) {
msgDesc := message.ProtoReflect().Descriptor()
msgName := msgDesc.FullName()
typeName := sanitizeProtoName(string(msgName))
if fd, found := pbdb.revFileDescriptorMap[typeName]; found {
return fd, nil
}
return pbdb.RegisterDescriptor(msgDesc.ParentFile())
}
// DescribeEnum takes a qualified enum name and returns an `EnumDescription` if it exists in the
// `pb.Db`.
func (pbdb *Db) DescribeEnum(enumName string) (*EnumValueDescription, bool) {
enumName = sanitizeProtoName(enumName)
if fd, found := pbdb.revFileDescriptorMap[enumName]; found {
return fd.GetEnumDescription(enumName)
}
return nil, false
}
// DescribeType returns a `TypeDescription` for the `typeName` if it exists in the `pb.Db`.
func (pbdb *Db) DescribeType(typeName string) (*TypeDescription, bool) {
typeName = sanitizeProtoName(typeName)
if fd, found := pbdb.revFileDescriptorMap[typeName]; found {
return fd.GetTypeDescription(typeName)
}
return nil, false
}
// CollectFileDescriptorSet builds a file descriptor set associated with the file where the input
// message is declared.
func CollectFileDescriptorSet(message proto.Message) map[string]protoreflect.FileDescriptor {
fdMap := map[string]protoreflect.FileDescriptor{}
parentFile := message.ProtoReflect().Descriptor().ParentFile()
fdMap[parentFile.Path()] = parentFile
// Initialize list of dependencies
deps := make([]protoreflect.FileImport, parentFile.Imports().Len())
for i := 0; i < parentFile.Imports().Len(); i++ {
deps[i] = parentFile.Imports().Get(i)
}
// Expand list for new dependencies
for i := 0; i < len(deps); i++ {
dep := deps[i]
if _, found := fdMap[dep.Path()]; found {
continue
}
fdMap[dep.Path()] = dep.FileDescriptor
for j := 0; j < dep.FileDescriptor.Imports().Len(); j++ {
deps = append(deps, dep.FileDescriptor.Imports().Get(j))
}
}
return fdMap
}
func init() {
// Describe well-known types to ensure they can always be resolved by the check and interpret
// execution phases.
//
// The following subset of message types is enough to ensure that all well-known types can
// resolved in the runtime, since describing the value results in describing the whole file
// where the message is declared.
DefaultDb.RegisterMessage(&anypb.Any{})
DefaultDb.RegisterMessage(&durpb.Duration{})
DefaultDb.RegisterMessage(&emptypb.Empty{})
DefaultDb.RegisterMessage(&tspb.Timestamp{})
DefaultDb.RegisterMessage(&structpb.Value{})
DefaultDb.RegisterMessage(&wrapperspb.BoolValue{})
}
// Copyright 2018 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package pb
import (
"fmt"
"reflect"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/reflect/protoreflect"
exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1"
dynamicpb "google.golang.org/protobuf/types/dynamicpb"
anypb "google.golang.org/protobuf/types/known/anypb"
dpb "google.golang.org/protobuf/types/known/durationpb"
structpb "google.golang.org/protobuf/types/known/structpb"
tpb "google.golang.org/protobuf/types/known/timestamppb"
wrapperspb "google.golang.org/protobuf/types/known/wrapperspb"
)
// description is a private interface used to make it convenient to perform type unwrapping at
// the TypeDescription or FieldDescription level.
type description interface {
// Zero returns an empty immutable protobuf message when the description is a protobuf message
// type.
Zero() proto.Message
}
// newTypeDescription produces a TypeDescription value for the fully-qualified proto type name
// with a given descriptor.
func newTypeDescription(typeName string, desc protoreflect.MessageDescriptor, extensions extensionMap) *TypeDescription {
msgType := dynamicpb.NewMessageType(desc)
msgZero := dynamicpb.NewMessage(desc)
fieldMap := map[string]*FieldDescription{}
fields := desc.Fields()
for i := 0; i < fields.Len(); i++ {
f := fields.Get(i)
fieldMap[string(f.Name())] = newFieldDescription(f)
}
return &TypeDescription{
typeName: typeName,
desc: desc,
msgType: msgType,
fieldMap: fieldMap,
extensions: extensions,
reflectType: reflectTypeOf(msgZero),
zeroMsg: zeroValueOf(msgZero),
}
}
// TypeDescription is a collection of type metadata relevant to expression
// checking and evaluation.
type TypeDescription struct {
typeName string
desc protoreflect.MessageDescriptor
msgType protoreflect.MessageType
fieldMap map[string]*FieldDescription
extensions extensionMap
reflectType reflect.Type
zeroMsg proto.Message
}
// Copy copies the type description with updated references to the Db.
func (td *TypeDescription) Copy(pbdb *Db) *TypeDescription {
return &TypeDescription{
typeName: td.typeName,
desc: td.desc,
msgType: td.msgType,
fieldMap: td.fieldMap,
extensions: pbdb.extensions,
reflectType: td.reflectType,
zeroMsg: td.zeroMsg,
}
}
// FieldMap returns a string field name to FieldDescription map.
func (td *TypeDescription) FieldMap() map[string]*FieldDescription {
return td.fieldMap
}
// FieldByName returns (FieldDescription, true) if the field name is declared within the type.
func (td *TypeDescription) FieldByName(name string) (*FieldDescription, bool) {
fd, found := td.fieldMap[name]
if found {
return fd, true
}
extFieldMap, found := td.extensions[td.typeName]
if !found {
return nil, false
}
fd, found = extFieldMap[name]
return fd, found
}
// MaybeUnwrap accepts a proto message as input and unwraps it to a primitive CEL type if possible.
//
// This method returns the unwrapped value and 'true', else the original value and 'false'.
func (td *TypeDescription) MaybeUnwrap(msg proto.Message) (any, bool, error) {
return unwrap(td, msg)
}
// Name returns the fully-qualified name of the type.
func (td *TypeDescription) Name() string {
return string(td.desc.FullName())
}
// New returns a mutable proto message
func (td *TypeDescription) New() protoreflect.Message {
return td.msgType.New()
}
// ReflectType returns the Golang reflect.Type for this type.
func (td *TypeDescription) ReflectType() reflect.Type {
return td.reflectType
}
// Zero returns the zero proto.Message value for this type.
func (td *TypeDescription) Zero() proto.Message {
return td.zeroMsg
}
// newFieldDescription creates a new field description from a protoreflect.FieldDescriptor.
func newFieldDescription(fieldDesc protoreflect.FieldDescriptor) *FieldDescription {
var reflectType reflect.Type
var zeroMsg proto.Message
switch fieldDesc.Kind() {
case protoreflect.EnumKind:
reflectType = reflectTypeOf(protoreflect.EnumNumber(0))
case protoreflect.GroupKind, protoreflect.MessageKind:
zeroMsg = dynamicpb.NewMessage(fieldDesc.Message())
reflectType = reflectTypeOf(zeroMsg)
default:
reflectType = reflectTypeOf(fieldDesc.Default().Interface())
if fieldDesc.IsList() {
var elemValue protoreflect.Value
if fieldDesc.IsExtension() {
et := dynamicpb.NewExtensionType(fieldDesc)
elemValue = et.New().List().NewElement()
} else {
parentMsgType := fieldDesc.ContainingMessage()
parentMsg := dynamicpb.NewMessage(parentMsgType)
listField := parentMsg.NewField(fieldDesc).List()
elemValue = listField.NewElement()
}
elem := elemValue.Interface()
switch elemType := elem.(type) {
case protoreflect.Message:
elem = elemType.Interface()
}
reflectType = reflectTypeOf(elem)
}
}
// Ensure the list type is appropriately reflected as a Go-native list.
if fieldDesc.IsList() {
reflectType = reflect.SliceOf(reflectType)
}
var keyType, valType *FieldDescription
if fieldDesc.IsMap() {
keyType = newFieldDescription(fieldDesc.MapKey())
valType = newFieldDescription(fieldDesc.MapValue())
}
return &FieldDescription{
desc: fieldDesc,
KeyType: keyType,
ValueType: valType,
reflectType: reflectType,
zeroMsg: zeroValueOf(zeroMsg),
}
}
// FieldDescription holds metadata related to fields declared within a type.
type FieldDescription struct {
// KeyType holds the key FieldDescription for map fields.
KeyType *FieldDescription
// ValueType holds the value FieldDescription for map fields.
ValueType *FieldDescription
desc protoreflect.FieldDescriptor
reflectType reflect.Type
zeroMsg proto.Message
}
// CheckedType returns the type-definition used at type-check time.
func (fd *FieldDescription) CheckedType() *exprpb.Type {
if fd.desc.IsMap() {
return &exprpb.Type{
TypeKind: &exprpb.Type_MapType_{
MapType: &exprpb.Type_MapType{
KeyType: fd.KeyType.typeDefToType(),
ValueType: fd.ValueType.typeDefToType(),
},
},
}
}
if fd.desc.IsList() {
return &exprpb.Type{
TypeKind: &exprpb.Type_ListType_{
ListType: &exprpb.Type_ListType{
ElemType: fd.typeDefToType()}}}
}
return fd.typeDefToType()
}
// Descriptor returns the protoreflect.FieldDescriptor for this type.
func (fd *FieldDescription) Descriptor() protoreflect.FieldDescriptor {
return fd.desc
}
// IsSet returns whether the field is set on the target value, per the proto presence conventions
// of proto2 or proto3 accordingly.
//
// This function implements the FieldType.IsSet function contract which can be used to operate on
// more than just protobuf field accesses; however, the target here must be a protobuf.Message.
func (fd *FieldDescription) IsSet(target any) bool {
switch v := target.(type) {
case proto.Message:
pbRef := v.ProtoReflect()
pbDesc := pbRef.Descriptor()
if pbDesc == fd.desc.ContainingMessage() {
// When the target protobuf shares the same message descriptor instance as the field
// descriptor, use the cached field descriptor value.
return pbRef.Has(fd.desc)
}
// Otherwise, fallback to a dynamic lookup of the field descriptor from the target
// instance as an attempt to use the cached field descriptor will result in a panic.
return pbRef.Has(pbDesc.Fields().ByName(protoreflect.Name(fd.Name())))
default:
return false
}
}
// GetFrom returns the accessor method associated with the field on the proto generated struct.
//
// If the field is not set, the proto default value is returned instead.
//
// This function implements the FieldType.GetFrom function contract which can be used to operate
// on more than just protobuf field accesses; however, the target here must be a protobuf.Message.
func (fd *FieldDescription) GetFrom(target any) (any, error) {
v, ok := target.(proto.Message)
if !ok {
return nil, fmt.Errorf("unsupported field selection target: (%T)%v", target, target)
}
pbRef := v.ProtoReflect()
pbDesc := pbRef.Descriptor()
var fieldVal any
if pbDesc == fd.desc.ContainingMessage() {
// When the target protobuf shares the same message descriptor instance as the field
// descriptor, use the cached field descriptor value.
fieldVal = pbRef.Get(fd.desc).Interface()
} else {
// Otherwise, fallback to a dynamic lookup of the field descriptor from the target
// instance as an attempt to use the cached field descriptor will result in a panic.
fieldVal = pbRef.Get(pbDesc.Fields().ByName(protoreflect.Name(fd.Name()))).Interface()
}
switch fv := fieldVal.(type) {
// Fast-path return for primitive types.
case bool, []byte, float32, float64, int32, int64, string, uint32, uint64, protoreflect.List:
return fv, nil
case protoreflect.EnumNumber:
return int64(fv), nil
case protoreflect.Map:
// Return a wrapper around the protobuf-reflected Map types which carries additional
// information about the key and value definitions of the map.
return &Map{Map: fv, KeyType: fd.KeyType, ValueType: fd.ValueType}, nil
case protoreflect.Message:
// Make sure to unwrap well-known protobuf types before returning.
unwrapped, _, err := fd.MaybeUnwrapDynamic(fv)
return unwrapped, err
default:
return fv, nil
}
}
// IsEnum returns true if the field type refers to an enum value.
func (fd *FieldDescription) IsEnum() bool {
return fd.ProtoKind() == protoreflect.EnumKind
}
// IsMap returns true if the field is of map type.
func (fd *FieldDescription) IsMap() bool {
return fd.desc.IsMap()
}
// IsMessage returns true if the field is of message type.
func (fd *FieldDescription) IsMessage() bool {
kind := fd.ProtoKind()
return kind == protoreflect.MessageKind || kind == protoreflect.GroupKind
}
// IsOneof returns true if the field is declared within a oneof block.
func (fd *FieldDescription) IsOneof() bool {
return fd.desc.ContainingOneof() != nil
}
// IsList returns true if the field is a repeated value.
//
// This method will also return true for map values, so check whether the
// field is also a map.
func (fd *FieldDescription) IsList() bool {
return fd.desc.IsList()
}
// MaybeUnwrapDynamic takes the reflected protoreflect.Message and determines whether the
// value can be unwrapped to a more primitive CEL type.
//
// This function returns the unwrapped value and 'true' on success, or the original value
// and 'false' otherwise.
func (fd *FieldDescription) MaybeUnwrapDynamic(msg protoreflect.Message) (any, bool, error) {
return unwrapDynamic(fd, msg)
}
// Name returns the CamelCase name of the field within the proto-based struct.
func (fd *FieldDescription) Name() string {
return string(fd.desc.Name())
}
// ProtoKind returns the protobuf reflected kind of the field.
func (fd *FieldDescription) ProtoKind() protoreflect.Kind {
return fd.desc.Kind()
}
// ReflectType returns the Golang reflect.Type for this field.
func (fd *FieldDescription) ReflectType() reflect.Type {
return fd.reflectType
}
// String returns the fully qualified name of the field within its type as well as whether the
// field occurs within a oneof.
func (fd *FieldDescription) String() string {
return fmt.Sprintf("%v.%s `oneof=%t`", fd.desc.ContainingMessage().FullName(), fd.Name(), fd.IsOneof())
}
// Zero returns the zero value for the protobuf message represented by this field.
//
// If the field is not a proto.Message type, the zero value is nil.
func (fd *FieldDescription) Zero() proto.Message {
return fd.zeroMsg
}
func (fd *FieldDescription) typeDefToType() *exprpb.Type {
if fd.IsMessage() {
msgType := string(fd.desc.Message().FullName())
if wk, found := CheckedWellKnowns[msgType]; found {
return wk
}
return checkedMessageType(msgType)
}
if fd.IsEnum() {
return checkedInt
}
return CheckedPrimitives[fd.ProtoKind()]
}
// Map wraps the protoreflect.Map object with a key and value FieldDescription for use in
// retrieving individual elements within CEL value data types.
type Map struct {
protoreflect.Map
KeyType *FieldDescription
ValueType *FieldDescription
}
func checkedMessageType(name string) *exprpb.Type {
return &exprpb.Type{
TypeKind: &exprpb.Type_MessageType{MessageType: name}}
}
func checkedPrimitive(primitive exprpb.Type_PrimitiveType) *exprpb.Type {
return &exprpb.Type{
TypeKind: &exprpb.Type_Primitive{Primitive: primitive}}
}
func checkedWellKnown(wellKnown exprpb.Type_WellKnownType) *exprpb.Type {
return &exprpb.Type{
TypeKind: &exprpb.Type_WellKnown{WellKnown: wellKnown}}
}
func checkedWrap(t *exprpb.Type) *exprpb.Type {
return &exprpb.Type{
TypeKind: &exprpb.Type_Wrapper{Wrapper: t.GetPrimitive()}}
}
// unwrap unwraps the provided proto.Message value, potentially based on the description if the
// input message is a *dynamicpb.Message which obscures the typing information from Go.
//
// Returns the unwrapped value and 'true' if unwrapped, otherwise the input value and 'false'.
func unwrap(desc description, msg proto.Message) (any, bool, error) {
switch v := msg.(type) {
case *anypb.Any:
dynMsg, err := v.UnmarshalNew()
if err != nil {
return v, false, err
}
return unwrapDynamic(desc, dynMsg.ProtoReflect())
case *dynamicpb.Message:
return unwrapDynamic(desc, v)
case *dpb.Duration:
return v.AsDuration(), true, nil
case *tpb.Timestamp:
return v.AsTime(), true, nil
case *structpb.Value:
switch v.GetKind().(type) {
case *structpb.Value_BoolValue:
return v.GetBoolValue(), true, nil
case *structpb.Value_ListValue:
return v.GetListValue(), true, nil
case *structpb.Value_NullValue:
return structpb.NullValue_NULL_VALUE, true, nil
case *structpb.Value_NumberValue:
return v.GetNumberValue(), true, nil
case *structpb.Value_StringValue:
return v.GetStringValue(), true, nil
case *structpb.Value_StructValue:
return v.GetStructValue(), true, nil
default:
return structpb.NullValue_NULL_VALUE, true, nil
}
case *wrapperspb.BoolValue:
if v == nil {
return nil, true, nil
}
return v.GetValue(), true, nil
case *wrapperspb.BytesValue:
if v == nil {
return nil, true, nil
}
return v.GetValue(), true, nil
case *wrapperspb.DoubleValue:
if v == nil {
return nil, true, nil
}
return v.GetValue(), true, nil
case *wrapperspb.FloatValue:
if v == nil {
return nil, true, nil
}
return float64(v.GetValue()), true, nil
case *wrapperspb.Int32Value:
if v == nil {
return nil, true, nil
}
return int64(v.GetValue()), true, nil
case *wrapperspb.Int64Value:
if v == nil {
return nil, true, nil
}
return v.GetValue(), true, nil
case *wrapperspb.StringValue:
if v == nil {
return nil, true, nil
}
return v.GetValue(), true, nil
case *wrapperspb.UInt32Value:
if v == nil {
return nil, true, nil
}
return uint64(v.GetValue()), true, nil
case *wrapperspb.UInt64Value:
if v == nil {
return nil, true, nil
}
return v.GetValue(), true, nil
}
return msg, false, nil
}
// unwrapDynamic unwraps a reflected protobuf Message value.
//
// Returns the unwrapped value and 'true' if unwrapped, otherwise the input value and 'false'.
func unwrapDynamic(desc description, refMsg protoreflect.Message) (any, bool, error) {
msg := refMsg.Interface()
if !refMsg.IsValid() {
msg = desc.Zero()
}
// In order to ensure that these wrapped types match the expectations of the CEL type system
// the dynamicpb.Message must be merged with an protobuf instance of the well-known type value.
typeName := string(refMsg.Descriptor().FullName())
switch typeName {
case "google.protobuf.Any":
// Note, Any values require further unwrapping; however, this unwrapping may or may not
// be to a well-known type. If the unwrapped value is a well-known type it will be further
// unwrapped before being returned to the caller. Otherwise, the dynamic protobuf object
// represented by the Any will be returned.
unwrappedAny := &anypb.Any{}
err := Merge(unwrappedAny, msg)
if err != nil {
return nil, false, fmt.Errorf("unwrap dynamic field failed: %v", err)
}
dynMsg, err := unwrappedAny.UnmarshalNew()
if err != nil {
// Allow the error to move further up the stack as it should result in an type
// conversion error if the caller does not recover it somehow.
return nil, false, fmt.Errorf("unmarshal dynamic any failed: %v", err)
}
// Attempt to unwrap the dynamic type, otherwise return the dynamic message.
unwrapped, nested, err := unwrapDynamic(desc, dynMsg.ProtoReflect())
if err == nil && nested {
return unwrapped, true, nil
}
return dynMsg, true, err
case "google.protobuf.BoolValue",
"google.protobuf.BytesValue",
"google.protobuf.DoubleValue",
"google.protobuf.FloatValue",
"google.protobuf.Int32Value",
"google.protobuf.Int64Value",
"google.protobuf.StringValue",
"google.protobuf.UInt32Value",
"google.protobuf.UInt64Value":
// The msg value is ignored when dealing with wrapper types as they have a null or value
// behavior, rather than the standard zero value behavior of other proto message types.
if !refMsg.IsValid() {
return structpb.NullValue_NULL_VALUE, true, nil
}
valueField := refMsg.Descriptor().Fields().ByName("value")
return refMsg.Get(valueField).Interface(), true, nil
case "google.protobuf.Duration":
unwrapped := &dpb.Duration{}
err := Merge(unwrapped, msg)
if err != nil {
return nil, false, err
}
return unwrapped.AsDuration(), true, nil
case "google.protobuf.ListValue":
unwrapped := &structpb.ListValue{}
err := Merge(unwrapped, msg)
if err != nil {
return nil, false, err
}
return unwrapped, true, nil
case "google.protobuf.NullValue":
return structpb.NullValue_NULL_VALUE, true, nil
case "google.protobuf.Struct":
unwrapped := &structpb.Struct{}
err := Merge(unwrapped, msg)
if err != nil {
return nil, false, err
}
return unwrapped, true, nil
case "google.protobuf.Timestamp":
unwrapped := &tpb.Timestamp{}
err := Merge(unwrapped, msg)
if err != nil {
return nil, false, err
}
return unwrapped.AsTime(), true, nil
case "google.protobuf.Value":
unwrapped := &structpb.Value{}
err := Merge(unwrapped, msg)
if err != nil {
return nil, false, err
}
return unwrap(desc, unwrapped)
}
return msg, false, nil
}
// reflectTypeOf intercepts the reflect.Type call to ensure that dynamicpb.Message types preserve
// well-known protobuf reflected types expected by the CEL type system.
func reflectTypeOf(val any) reflect.Type {
switch v := val.(type) {
case proto.Message:
return reflect.TypeOf(zeroValueOf(v))
default:
return reflect.TypeOf(v)
}
}
// zeroValueOf will return the strongest possible proto.Message representing the default protobuf
// message value of the input msg type.
func zeroValueOf(msg proto.Message) proto.Message {
if msg == nil {
return nil
}
typeName := string(msg.ProtoReflect().Descriptor().FullName())
zeroVal, found := zeroValueMap[typeName]
if found {
return zeroVal
}
return msg
}
var (
jsonValueTypeURL = "types.googleapis.com/google.protobuf.Value"
zeroValueMap = map[string]proto.Message{
"google.protobuf.Any": &anypb.Any{TypeUrl: jsonValueTypeURL},
"google.protobuf.Duration": &dpb.Duration{},
"google.protobuf.ListValue": &structpb.ListValue{},
"google.protobuf.Struct": &structpb.Struct{},
"google.protobuf.Timestamp": &tpb.Timestamp{},
"google.protobuf.Value": &structpb.Value{},
"google.protobuf.BoolValue": wrapperspb.Bool(false),
"google.protobuf.BytesValue": wrapperspb.Bytes([]byte{}),
"google.protobuf.DoubleValue": wrapperspb.Double(0.0),
"google.protobuf.FloatValue": wrapperspb.Float(0.0),
"google.protobuf.Int32Value": wrapperspb.Int32(0),
"google.protobuf.Int64Value": wrapperspb.Int64(0),
"google.protobuf.StringValue": wrapperspb.String(""),
"google.protobuf.UInt32Value": wrapperspb.UInt32(0),
"google.protobuf.UInt64Value": wrapperspb.UInt64(0),
}
)
// Copyright 2018 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package types
import (
"fmt"
"reflect"
"time"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/reflect/protoreflect"
"github.com/google/cel-go/common/types/pb"
"github.com/google/cel-go/common/types/ref"
"github.com/google/cel-go/common/types/traits"
exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1"
anypb "google.golang.org/protobuf/types/known/anypb"
dpb "google.golang.org/protobuf/types/known/durationpb"
structpb "google.golang.org/protobuf/types/known/structpb"
tpb "google.golang.org/protobuf/types/known/timestamppb"
)
// Adapter converts native Go values of varying type and complexity to equivalent CEL values.
type Adapter = ref.TypeAdapter
// Provider specifies functions for creating new object instances and for resolving
// enum values by name.
type Provider interface {
// EnumValue returns the numeric value of the given enum value name.
EnumValue(enumName string) ref.Val
// FindIdent takes a qualified identifier name and returns a ref.Val if one exists.
FindIdent(identName string) (ref.Val, bool)
// FindStructType returns the Type give a qualified type name.
//
// For historical reasons, only struct types are expected to be returned through this
// method, and the type values are expected to be wrapped in a TypeType instance using
// TypeTypeWithParam(<structType>).
//
// Returns false if not found.
FindStructType(structType string) (*Type, bool)
// FindStructFieldNames returns thet field names associated with the type, if the type
// is found.
FindStructFieldNames(structType string) ([]string, bool)
// FieldStructFieldType returns the field type for a checked type value. Returns
// false if the field could not be found.
FindStructFieldType(structType, fieldName string) (*FieldType, bool)
// NewValue creates a new type value from a qualified name and map of field
// name to value.
//
// Note, for each value, the Val.ConvertToNative function will be invoked
// to convert the Val to the field's native type. If an error occurs during
// conversion, the NewValue will be a types.Err.
NewValue(structType string, fields map[string]ref.Val) ref.Val
}
// FieldType represents a field's type value and whether that field supports presence detection.
type FieldType struct {
// Type of the field as a CEL native type value.
Type *Type
// IsSet indicates whether the field is set on an input object.
IsSet ref.FieldTester
// GetFrom retrieves the field value on the input object, if set.
GetFrom ref.FieldGetter
}
// Registry provides type information for a set of registered types.
type Registry struct {
revTypeMap map[string]*Type
pbdb *pb.Db
}
// NewRegistry accepts a list of proto message instances and returns a type
// provider which can create new instances of the provided message or any
// message that proto depends upon in its FileDescriptor.
func NewRegistry(types ...proto.Message) (*Registry, error) {
p := &Registry{
revTypeMap: make(map[string]*Type),
pbdb: pb.NewDb(),
}
err := p.RegisterType(
BoolType,
BytesType,
DoubleType,
DurationType,
IntType,
ListType,
MapType,
NullType,
StringType,
TimestampType,
TypeType,
UintType)
if err != nil {
return nil, err
}
// This block ensures that the well-known protobuf types are registered by default.
for _, fd := range p.pbdb.FileDescriptions() {
err = p.registerAllTypes(fd)
if err != nil {
return nil, err
}
}
for _, msgType := range types {
err = p.RegisterMessage(msgType)
if err != nil {
return nil, err
}
}
return p, nil
}
// NewEmptyRegistry returns a registry which is completely unconfigured.
func NewEmptyRegistry() *Registry {
return &Registry{
revTypeMap: make(map[string]*Type),
pbdb: pb.NewDb(),
}
}
// Copy copies the current state of the registry into its own memory space.
func (p *Registry) Copy() *Registry {
copy := &Registry{
revTypeMap: make(map[string]*Type),
pbdb: p.pbdb.Copy(),
}
for k, v := range p.revTypeMap {
copy.revTypeMap[k] = v
}
return copy
}
// EnumValue returns the numeric value of the given enum value name.
func (p *Registry) EnumValue(enumName string) ref.Val {
enumVal, found := p.pbdb.DescribeEnum(enumName)
if !found {
return NewErr("unknown enum name '%s'", enumName)
}
return Int(enumVal.Value())
}
// FindFieldType returns the field type for a checked type value. Returns false if
// the field could not be found.
//
// Deprecated: use FindStructFieldType
func (p *Registry) FindFieldType(structType, fieldName string) (*ref.FieldType, bool) {
msgType, found := p.pbdb.DescribeType(structType)
if !found {
return nil, false
}
field, found := msgType.FieldByName(fieldName)
if !found {
return nil, false
}
return &ref.FieldType{
Type: field.CheckedType(),
IsSet: field.IsSet,
GetFrom: field.GetFrom}, true
}
// FindStructFieldNames returns the set of field names for the given struct type,
// if the type exists in the registry.
func (p *Registry) FindStructFieldNames(structType string) ([]string, bool) {
msgType, found := p.pbdb.DescribeType(structType)
if !found {
return []string{}, false
}
fieldMap := msgType.FieldMap()
fields := make([]string, len(fieldMap))
idx := 0
for f := range fieldMap {
fields[idx] = f
idx++
}
return fields, true
}
// FindStructFieldType returns the field type for a checked type value. Returns
// false if the field could not be found.
func (p *Registry) FindStructFieldType(structType, fieldName string) (*FieldType, bool) {
msgType, found := p.pbdb.DescribeType(structType)
if !found {
return nil, false
}
field, found := msgType.FieldByName(fieldName)
if !found {
return nil, false
}
return &FieldType{
Type: fieldDescToCELType(field),
IsSet: field.IsSet,
GetFrom: field.GetFrom}, true
}
// FindIdent takes a qualified identifier name and returns a ref.Val if one exists.
func (p *Registry) FindIdent(identName string) (ref.Val, bool) {
if t, found := p.revTypeMap[identName]; found {
return t, true
}
if enumVal, found := p.pbdb.DescribeEnum(identName); found {
return Int(enumVal.Value()), true
}
return nil, false
}
// FindType looks up the Type given a qualified typeName. Returns false if not found.
//
// Deprecated: use FindStructType
func (p *Registry) FindType(structType string) (*exprpb.Type, bool) {
if _, found := p.pbdb.DescribeType(structType); !found {
return nil, false
}
if structType != "" && structType[0] == '.' {
structType = structType[1:]
}
return &exprpb.Type{
TypeKind: &exprpb.Type_Type{
Type: &exprpb.Type{
TypeKind: &exprpb.Type_MessageType{
MessageType: structType}}}}, true
}
// FindStructType returns the Type give a qualified type name.
//
// For historical reasons, only struct types are expected to be returned through this
// method, and the type values are expected to be wrapped in a TypeType instance using
// TypeTypeWithParam(<structType>).
//
// Returns false if not found.
func (p *Registry) FindStructType(structType string) (*Type, bool) {
if _, found := p.pbdb.DescribeType(structType); !found {
return nil, false
}
if structType != "" && structType[0] == '.' {
structType = structType[1:]
}
return NewTypeTypeWithParam(NewObjectType(structType)), true
}
// NewValue creates a new type value from a qualified name and map of field
// name to value.
//
// Note, for each value, the Val.ConvertToNative function will be invoked
// to convert the Val to the field's native type. If an error occurs during
// conversion, the NewValue will be a types.Err.
func (p *Registry) NewValue(structType string, fields map[string]ref.Val) ref.Val {
td, found := p.pbdb.DescribeType(structType)
if !found {
return NewErr("unknown type '%s'", structType)
}
msg := td.New()
fieldMap := td.FieldMap()
for name, value := range fields {
field, found := fieldMap[name]
if !found {
return NewErr("no such field: %s", name)
}
err := msgSetField(msg, field, value)
if err != nil {
return &Err{error: err}
}
}
return p.NativeToValue(msg.Interface())
}
// RegisterDescriptor registers the contents of a protocol buffer `FileDescriptor`.
func (p *Registry) RegisterDescriptor(fileDesc protoreflect.FileDescriptor) error {
fd, err := p.pbdb.RegisterDescriptor(fileDesc)
if err != nil {
return err
}
return p.registerAllTypes(fd)
}
// RegisterMessage registers a protocol buffer message and its dependencies.
func (p *Registry) RegisterMessage(message proto.Message) error {
fd, err := p.pbdb.RegisterMessage(message)
if err != nil {
return err
}
return p.registerAllTypes(fd)
}
// RegisterType registers a type value with the provider which ensures the provider is aware of how to
// map the type to an identifier.
//
// If the `ref.Type` value is a `*types.Type` it will be registered directly by its runtime type name.
// If the `ref.Type` value is not a `*types.Type` instance, a `*types.Type` instance which reflects the
// traits present on the input and the runtime type name. By default this foreign type will be treated
// as a types.StructKind. To avoid potential issues where the `ref.Type` values does not match the
// generated `*types.Type` instance, consider always using the `*types.Type` to represent type extensions
// to CEL, even when they're not based on protobuf types.
func (p *Registry) RegisterType(types ...ref.Type) error {
for _, t := range types {
celType := maybeForeignType(t)
existing, found := p.revTypeMap[t.TypeName()]
if !found {
p.revTypeMap[t.TypeName()] = celType
continue
}
if !existing.IsEquivalentType(celType) {
return fmt.Errorf("type registration conflict. found: %v, input: %v", existing, celType)
}
if existing.traitMask != celType.traitMask {
return fmt.Errorf(
"type registered with conflicting traits: %v with traits %v, input: %v",
existing.TypeName(), existing.traitMask, celType.traitMask)
}
}
return nil
}
// NativeToValue converts various "native" types to ref.Val with this specific implementation
// providing support for custom proto-based types.
//
// This method should be the inverse of ref.Val.ConvertToNative.
func (p *Registry) NativeToValue(value any) ref.Val {
if val, found := nativeToValue(p, value); found {
return val
}
switch v := value.(type) {
case proto.Message:
typeName := string(v.ProtoReflect().Descriptor().FullName())
td, found := p.pbdb.DescribeType(typeName)
if !found {
return NewErr("unknown type: '%s'", typeName)
}
unwrapped, isUnwrapped, err := td.MaybeUnwrap(v)
if err != nil {
return UnsupportedRefValConversionErr(v)
}
if isUnwrapped {
return p.NativeToValue(unwrapped)
}
typeVal, found := p.FindIdent(typeName)
if !found {
return NewErr("unknown type: '%s'", typeName)
}
return NewObject(p, td, typeVal, v)
case *pb.Map:
return NewProtoMap(p, v)
case protoreflect.List:
return NewProtoList(p, v)
case protoreflect.Message:
return p.NativeToValue(v.Interface())
case protoreflect.Value:
return p.NativeToValue(v.Interface())
}
return UnsupportedRefValConversionErr(value)
}
func (p *Registry) registerAllTypes(fd *pb.FileDescription) error {
for _, typeName := range fd.GetTypeNames() {
// skip well-known type names since they're automatically sanitized
// during NewObjectType() calls.
if _, found := checkedWellKnowns[typeName]; found {
continue
}
err := p.RegisterType(NewObjectTypeValue(typeName))
if err != nil {
return err
}
}
return nil
}
func fieldDescToCELType(field *pb.FieldDescription) *Type {
if field.IsMap() {
return NewMapType(
singularFieldDescToCELType(field.KeyType),
singularFieldDescToCELType(field.ValueType))
}
if field.IsList() {
return NewListType(singularFieldDescToCELType(field))
}
return singularFieldDescToCELType(field)
}
func singularFieldDescToCELType(field *pb.FieldDescription) *Type {
if field.IsMessage() {
return NewObjectType(string(field.Descriptor().Message().FullName()))
}
if field.IsEnum() {
return IntType
}
return ProtoCELPrimitives[field.ProtoKind()]
}
// defaultTypeAdapter converts go native types to CEL values.
type defaultTypeAdapter struct{}
var (
// DefaultTypeAdapter adapts canonical CEL types from their equivalent Go values.
DefaultTypeAdapter = &defaultTypeAdapter{}
)
// NativeToValue implements the ref.TypeAdapter interface.
func (a *defaultTypeAdapter) NativeToValue(value any) ref.Val {
if val, found := nativeToValue(a, value); found {
return val
}
return UnsupportedRefValConversionErr(value)
}
// nativeToValue returns the converted (ref.Val, true) of a conversion is found,
// otherwise (nil, false)
func nativeToValue(a Adapter, value any) (ref.Val, bool) {
switch v := value.(type) {
case nil:
return NullValue, true
case *Bool:
if v != nil {
return *v, true
}
case *Bytes:
if v != nil {
return *v, true
}
case *Double:
if v != nil {
return *v, true
}
case *Int:
if v != nil {
return *v, true
}
case *String:
if v != nil {
return *v, true
}
case *Uint:
if v != nil {
return *v, true
}
case bool:
return Bool(v), true
case int:
return Int(v), true
case int32:
return Int(v), true
case int64:
return Int(v), true
case uint:
return Uint(v), true
case uint32:
return Uint(v), true
case uint64:
return Uint(v), true
case float32:
return Double(v), true
case float64:
return Double(v), true
case string:
return String(v), true
case *dpb.Duration:
return Duration{Duration: v.AsDuration()}, true
case time.Duration:
return Duration{Duration: v}, true
case *tpb.Timestamp:
return Timestamp{Time: v.AsTime()}, true
case time.Time:
return Timestamp{Time: v}, true
case *bool:
if v != nil {
return Bool(*v), true
}
case *float32:
if v != nil {
return Double(*v), true
}
case *float64:
if v != nil {
return Double(*v), true
}
case *int:
if v != nil {
return Int(*v), true
}
case *int32:
if v != nil {
return Int(*v), true
}
case *int64:
if v != nil {
return Int(*v), true
}
case *string:
if v != nil {
return String(*v), true
}
case *uint:
if v != nil {
return Uint(*v), true
}
case *uint32:
if v != nil {
return Uint(*v), true
}
case *uint64:
if v != nil {
return Uint(*v), true
}
case []byte:
return Bytes(v), true
// specializations for common lists types.
case []string:
return NewStringList(a, v), true
case []ref.Val:
return NewRefValList(a, v), true
// specializations for common map types.
case map[string]string:
return NewStringStringMap(a, v), true
case map[string]any:
return NewStringInterfaceMap(a, v), true
case map[ref.Val]ref.Val:
return NewRefValMap(a, v), true
// additional specializations may be added upon request / need.
case *anypb.Any:
if v == nil {
return UnsupportedRefValConversionErr(v), true
}
unpackedAny, err := v.UnmarshalNew()
if err != nil {
return NewErr("anypb.UnmarshalNew() failed for type %q: %v", v.GetTypeUrl(), err), true
}
return a.NativeToValue(unpackedAny), true
case *structpb.NullValue, structpb.NullValue:
return NullValue, true
case *structpb.ListValue:
return NewJSONList(a, v), true
case *structpb.Struct:
return NewJSONStruct(a, v), true
case ref.Val:
return v, true
case protoreflect.EnumNumber:
return Int(v), true
case proto.Message:
if v == nil {
return UnsupportedRefValConversionErr(v), true
}
typeName := string(v.ProtoReflect().Descriptor().FullName())
td, found := pb.DefaultDb.DescribeType(typeName)
if !found {
return nil, false
}
val, unwrapped, err := td.MaybeUnwrap(v)
if err != nil {
return UnsupportedRefValConversionErr(v), true
}
if !unwrapped {
return nil, false
}
return a.NativeToValue(val), true
// Note: dynamicpb.Message implements the proto.Message _and_ protoreflect.Message interfaces
// which means that this case must appear after handling a proto.Message type.
case protoreflect.Message:
return a.NativeToValue(v.Interface()), true
default:
refValue := reflect.ValueOf(v)
if refValue.Kind() == reflect.Ptr {
if refValue.IsNil() {
return UnsupportedRefValConversionErr(v), true
}
refValue = refValue.Elem()
}
refKind := refValue.Kind()
switch refKind {
case reflect.Array, reflect.Slice:
if refValue.Type().Elem() == reflect.TypeOf(byte(0)) {
if refValue.CanAddr() {
return Bytes(refValue.Bytes()), true
}
tmp := reflect.New(refValue.Type())
tmp.Elem().Set(refValue)
return Bytes(tmp.Elem().Bytes()), true
}
return NewDynamicList(a, v), true
case reflect.Map:
return NewDynamicMap(a, v), true
// type aliases of primitive types cannot be asserted as that type, but rather need
// to be downcast to int32 before being converted to a CEL representation.
case reflect.Bool:
boolTupe := reflect.TypeOf(false)
return Bool(refValue.Convert(boolTupe).Interface().(bool)), true
case reflect.Int:
intType := reflect.TypeOf(int(0))
return Int(refValue.Convert(intType).Interface().(int)), true
case reflect.Int8:
intType := reflect.TypeOf(int8(0))
return Int(refValue.Convert(intType).Interface().(int8)), true
case reflect.Int16:
intType := reflect.TypeOf(int16(0))
return Int(refValue.Convert(intType).Interface().(int16)), true
case reflect.Int32:
intType := reflect.TypeOf(int32(0))
return Int(refValue.Convert(intType).Interface().(int32)), true
case reflect.Int64:
intType := reflect.TypeOf(int64(0))
return Int(refValue.Convert(intType).Interface().(int64)), true
case reflect.Uint:
uintType := reflect.TypeOf(uint(0))
return Uint(refValue.Convert(uintType).Interface().(uint)), true
case reflect.Uint8:
uintType := reflect.TypeOf(uint8(0))
return Uint(refValue.Convert(uintType).Interface().(uint8)), true
case reflect.Uint16:
uintType := reflect.TypeOf(uint16(0))
return Uint(refValue.Convert(uintType).Interface().(uint16)), true
case reflect.Uint32:
uintType := reflect.TypeOf(uint32(0))
return Uint(refValue.Convert(uintType).Interface().(uint32)), true
case reflect.Uint64:
uintType := reflect.TypeOf(uint64(0))
return Uint(refValue.Convert(uintType).Interface().(uint64)), true
case reflect.Float32:
doubleType := reflect.TypeOf(float32(0))
return Double(refValue.Convert(doubleType).Interface().(float32)), true
case reflect.Float64:
doubleType := reflect.TypeOf(float64(0))
return Double(refValue.Convert(doubleType).Interface().(float64)), true
case reflect.String:
stringType := reflect.TypeOf("")
return String(refValue.Convert(stringType).Interface().(string)), true
}
}
return nil, false
}
func msgSetField(target protoreflect.Message, field *pb.FieldDescription, val ref.Val) error {
if field.IsList() {
lv := target.NewField(field.Descriptor())
list, ok := val.(traits.Lister)
if !ok {
return unsupportedTypeConversionError(field, val)
}
err := msgSetListField(lv.List(), field, list)
if err != nil {
return err
}
target.Set(field.Descriptor(), lv)
return nil
}
if field.IsMap() {
mv := target.NewField(field.Descriptor())
mp, ok := val.(traits.Mapper)
if !ok {
return unsupportedTypeConversionError(field, val)
}
err := msgSetMapField(mv.Map(), field, mp)
if err != nil {
return err
}
target.Set(field.Descriptor(), mv)
return nil
}
v, err := val.ConvertToNative(field.ReflectType())
if err != nil {
return fieldTypeConversionError(field, err)
}
if v == nil {
return nil
}
switch pv := v.(type) {
case proto.Message:
v = pv.ProtoReflect()
}
target.Set(field.Descriptor(), protoreflect.ValueOf(v))
return nil
}
func msgSetListField(target protoreflect.List, listField *pb.FieldDescription, listVal traits.Lister) error {
elemReflectType := listField.ReflectType().Elem()
for i := Int(0); i < listVal.Size().(Int); i++ {
elem := listVal.Get(i)
elemVal, err := elem.ConvertToNative(elemReflectType)
if err != nil {
return fieldTypeConversionError(listField, err)
}
if elemVal == nil {
continue
}
switch ev := elemVal.(type) {
case proto.Message:
elemVal = ev.ProtoReflect()
}
target.Append(protoreflect.ValueOf(elemVal))
}
return nil
}
func msgSetMapField(target protoreflect.Map, mapField *pb.FieldDescription, mapVal traits.Mapper) error {
targetKeyType := mapField.KeyType.ReflectType()
targetValType := mapField.ValueType.ReflectType()
it := mapVal.Iterator()
for it.HasNext() == True {
key := it.Next()
val := mapVal.Get(key)
k, err := key.ConvertToNative(targetKeyType)
if err != nil {
return fieldTypeConversionError(mapField, err)
}
v, err := val.ConvertToNative(targetValType)
if err != nil {
return fieldTypeConversionError(mapField, err)
}
if v == nil {
continue
}
switch pv := v.(type) {
case proto.Message:
v = pv.ProtoReflect()
}
target.Set(protoreflect.ValueOf(k).MapKey(), protoreflect.ValueOf(v))
}
return nil
}
func unsupportedTypeConversionError(field *pb.FieldDescription, val ref.Val) error {
msgName := field.Descriptor().ContainingMessage().FullName()
return fmt.Errorf("unsupported field type for %v.%v: %v", msgName, field.Name(), val.Type())
}
func fieldTypeConversionError(field *pb.FieldDescription, err error) error {
msgName := field.Descriptor().ContainingMessage().FullName()
return fmt.Errorf("field type conversion error for %v.%v value type: %v", msgName, field.Name(), err)
}
var (
// ProtoCELPrimitives provides a map from the protoreflect Kind to the equivalent CEL type.
ProtoCELPrimitives = map[protoreflect.Kind]*Type{
protoreflect.BoolKind: BoolType,
protoreflect.BytesKind: BytesType,
protoreflect.DoubleKind: DoubleType,
protoreflect.FloatKind: DoubleType,
protoreflect.Int32Kind: IntType,
protoreflect.Int64Kind: IntType,
protoreflect.Sint32Kind: IntType,
protoreflect.Sint64Kind: IntType,
protoreflect.Uint32Kind: UintType,
protoreflect.Uint64Kind: UintType,
protoreflect.Fixed32Kind: UintType,
protoreflect.Fixed64Kind: UintType,
protoreflect.Sfixed32Kind: IntType,
protoreflect.Sfixed64Kind: IntType,
protoreflect.StringKind: StringType,
}
)
// Copyright 2018 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package types
import (
"fmt"
"reflect"
"regexp"
"strconv"
"strings"
"time"
"github.com/google/cel-go/common/overloads"
"github.com/google/cel-go/common/types/ref"
anypb "google.golang.org/protobuf/types/known/anypb"
structpb "google.golang.org/protobuf/types/known/structpb"
wrapperspb "google.golang.org/protobuf/types/known/wrapperspb"
)
// String type implementation which supports addition, comparison, matching,
// and size functions.
type String string
var (
stringOneArgOverloads = map[string]func(ref.Val, ref.Val) ref.Val{
overloads.Contains: StringContains,
overloads.EndsWith: StringEndsWith,
overloads.StartsWith: StringStartsWith,
}
stringWrapperType = reflect.TypeOf(&wrapperspb.StringValue{})
)
// Add implements traits.Adder.Add.
func (s String) Add(other ref.Val) ref.Val {
otherString, ok := other.(String)
if !ok {
return MaybeNoSuchOverloadErr(other)
}
return s + otherString
}
// Compare implements traits.Comparer.Compare.
func (s String) Compare(other ref.Val) ref.Val {
otherString, ok := other.(String)
if !ok {
return MaybeNoSuchOverloadErr(other)
}
return Int(strings.Compare(s.Value().(string), otherString.Value().(string)))
}
// ConvertToNative implements ref.Val.ConvertToNative.
func (s String) ConvertToNative(typeDesc reflect.Type) (any, error) {
switch typeDesc.Kind() {
case reflect.String:
return reflect.ValueOf(s).Convert(typeDesc).Interface(), nil
case reflect.Ptr:
switch typeDesc {
case anyValueType:
// Primitives must be wrapped before being set on an Any field.
return anypb.New(wrapperspb.String(string(s)))
case jsonValueType:
// Convert to a protobuf representation of a JSON String.
return structpb.NewStringValue(string(s)), nil
case stringWrapperType:
// Convert to a wrapperspb.StringValue.
return wrapperspb.String(string(s)), nil
}
if typeDesc.Elem().Kind() == reflect.String {
p := s.Value().(string)
return &p, nil
}
case reflect.Interface:
sv := s.Value()
if reflect.TypeOf(sv).Implements(typeDesc) {
return sv, nil
}
if reflect.TypeOf(s).Implements(typeDesc) {
return s, nil
}
}
return nil, fmt.Errorf(
"unsupported native conversion from string to '%v'", typeDesc)
}
// ConvertToType implements ref.Val.ConvertToType.
func (s String) ConvertToType(typeVal ref.Type) ref.Val {
switch typeVal {
case IntType:
if n, err := strconv.ParseInt(s.Value().(string), 10, 64); err == nil {
return Int(n)
}
case UintType:
if n, err := strconv.ParseUint(s.Value().(string), 10, 64); err == nil {
return Uint(n)
}
case DoubleType:
if n, err := strconv.ParseFloat(s.Value().(string), 64); err == nil {
return Double(n)
}
case BoolType:
if b, err := strconv.ParseBool(s.Value().(string)); err == nil {
return Bool(b)
}
case BytesType:
return Bytes(s)
case DurationType:
if d, err := time.ParseDuration(s.Value().(string)); err == nil {
return durationOf(d)
}
case TimestampType:
if t, err := time.Parse(time.RFC3339, s.Value().(string)); err == nil {
if t.Unix() < minUnixTime || t.Unix() > maxUnixTime {
return celErrTimestampOverflow
}
return timestampOf(t)
}
case StringType:
return s
case TypeType:
return StringType
}
return NewErr("type conversion error from '%s' to '%s'", StringType, typeVal)
}
// Equal implements ref.Val.Equal.
func (s String) Equal(other ref.Val) ref.Val {
otherString, ok := other.(String)
return Bool(ok && s == otherString)
}
// IsZeroValue returns true if the string is empty.
func (s String) IsZeroValue() bool {
return len(s) == 0
}
// Match implements traits.Matcher.Match.
func (s String) Match(pattern ref.Val) ref.Val {
pat, ok := pattern.(String)
if !ok {
return MaybeNoSuchOverloadErr(pattern)
}
matched, err := regexp.MatchString(pat.Value().(string), s.Value().(string))
if err != nil {
return &Err{error: err}
}
return Bool(matched)
}
// Receive implements traits.Receiver.Receive.
func (s String) Receive(function string, overload string, args []ref.Val) ref.Val {
switch len(args) {
case 1:
if f, found := stringOneArgOverloads[function]; found {
return f(s, args[0])
}
}
return NoSuchOverloadErr()
}
// Size implements traits.Sizer.Size.
func (s String) Size() ref.Val {
return Int(len([]rune(s.Value().(string))))
}
// Type implements ref.Val.Type.
func (s String) Type() ref.Type {
return StringType
}
// Value implements ref.Val.Value.
func (s String) Value() any {
return string(s)
}
func (s String) format(sb *strings.Builder) {
sb.WriteString(strconv.Quote(string(s)))
}
// StringContains returns whether the string contains a substring.
func StringContains(s, sub ref.Val) ref.Val {
str, ok := s.(String)
if !ok {
return MaybeNoSuchOverloadErr(s)
}
subStr, ok := sub.(String)
if !ok {
return MaybeNoSuchOverloadErr(sub)
}
return Bool(strings.Contains(string(str), string(subStr)))
}
// StringEndsWith returns whether the target string contains the input suffix.
func StringEndsWith(s, suf ref.Val) ref.Val {
str, ok := s.(String)
if !ok {
return MaybeNoSuchOverloadErr(s)
}
sufStr, ok := suf.(String)
if !ok {
return MaybeNoSuchOverloadErr(suf)
}
return Bool(strings.HasSuffix(string(str), string(sufStr)))
}
// StringStartsWith returns whether the target string contains the input prefix.
func StringStartsWith(s, pre ref.Val) ref.Val {
str, ok := s.(String)
if !ok {
return MaybeNoSuchOverloadErr(s)
}
preStr, ok := pre.(String)
if !ok {
return MaybeNoSuchOverloadErr(pre)
}
return Bool(strings.HasPrefix(string(str), string(preStr)))
}
// Copyright 2018 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package types
import (
"fmt"
"reflect"
"strconv"
"strings"
"time"
"github.com/google/cel-go/common/overloads"
"github.com/google/cel-go/common/types/ref"
anypb "google.golang.org/protobuf/types/known/anypb"
structpb "google.golang.org/protobuf/types/known/structpb"
tpb "google.golang.org/protobuf/types/known/timestamppb"
)
// Timestamp type implementation which supports add, compare, and subtract
// operations. Timestamps are also capable of participating in dynamic
// function dispatch to instance methods.
type Timestamp struct {
time.Time
}
func timestampOf(t time.Time) Timestamp {
// Note that this function does not validate that time.Time is in our supported range.
return Timestamp{Time: t}
}
const (
// The number of seconds between year 1 and year 1970. This is borrowed from
// https://golang.org/src/time/time.go.
unixToInternal int64 = (1969*365 + 1969/4 - 1969/100 + 1969/400) * (60 * 60 * 24)
// Number of seconds between `0001-01-01T00:00:00Z` and the Unix epoch.
minUnixTime int64 = -62135596800
// Number of seconds between `9999-12-31T23:59:59.999999999Z` and the Unix epoch.
maxUnixTime int64 = 253402300799
)
// Add implements traits.Adder.Add.
func (t Timestamp) Add(other ref.Val) ref.Val {
switch other.Type() {
case DurationType:
return other.(Duration).Add(t)
}
return MaybeNoSuchOverloadErr(other)
}
// Compare implements traits.Comparer.Compare.
func (t Timestamp) Compare(other ref.Val) ref.Val {
if TimestampType != other.Type() {
return MaybeNoSuchOverloadErr(other)
}
ts1 := t.Time
ts2 := other.(Timestamp).Time
switch {
case ts1.Before(ts2):
return IntNegOne
case ts1.After(ts2):
return IntOne
default:
return IntZero
}
}
// ConvertToNative implements ref.Val.ConvertToNative.
func (t Timestamp) ConvertToNative(typeDesc reflect.Type) (any, error) {
// If the timestamp is already assignable to the desired type return it.
if reflect.TypeOf(t.Time).AssignableTo(typeDesc) {
return t.Time, nil
}
if reflect.TypeOf(t).AssignableTo(typeDesc) {
return t, nil
}
switch typeDesc {
case anyValueType:
// Pack the underlying time as a tpb.Timestamp into an Any value.
return anypb.New(tpb.New(t.Time))
case jsonValueType:
// CEL follows the proto3 to JSON conversion which formats as an RFC 3339 encoded JSON
// string.
v := t.ConvertToType(StringType)
if IsError(v) {
return nil, v.(*Err)
}
return structpb.NewStringValue(string(v.(String))), nil
case timestampValueType:
// Unwrap the underlying tpb.Timestamp.
return tpb.New(t.Time), nil
}
return nil, fmt.Errorf("type conversion error from 'Timestamp' to '%v'", typeDesc)
}
// ConvertToType implements ref.Val.ConvertToType.
func (t Timestamp) ConvertToType(typeVal ref.Type) ref.Val {
switch typeVal {
case StringType:
return String(t.Format(time.RFC3339Nano))
case IntType:
// Return the Unix time in seconds since 1970
return Int(t.Unix())
case TimestampType:
return t
case TypeType:
return TimestampType
}
return NewErr("type conversion error from '%s' to '%s'", TimestampType, typeVal)
}
// Equal implements ref.Val.Equal.
func (t Timestamp) Equal(other ref.Val) ref.Val {
otherTime, ok := other.(Timestamp)
return Bool(ok && t.Time.Equal(otherTime.Time))
}
// IsZeroValue returns true if the timestamp is epoch 0.
func (t Timestamp) IsZeroValue() bool {
return t.IsZero()
}
// Receive implements traits.Receiver.Receive.
func (t Timestamp) Receive(function string, overload string, args []ref.Val) ref.Val {
switch len(args) {
case 0:
if f, found := timestampZeroArgOverloads[function]; found {
return f(t.Time)
}
case 1:
if f, found := timestampOneArgOverloads[function]; found {
return f(t.Time, args[0])
}
}
return NoSuchOverloadErr()
}
// Subtract implements traits.Subtractor.Subtract.
func (t Timestamp) Subtract(subtrahend ref.Val) ref.Val {
switch subtrahend.Type() {
case DurationType:
dur := subtrahend.(Duration)
val, err := subtractTimeDurationChecked(t.Time, dur.Duration)
if err != nil {
return WrapErr(err)
}
return timestampOf(val)
case TimestampType:
t2 := subtrahend.(Timestamp).Time
val, err := subtractTimeChecked(t.Time, t2)
if err != nil {
return WrapErr(err)
}
return durationOf(val)
}
return MaybeNoSuchOverloadErr(subtrahend)
}
// Type implements ref.Val.Type.
func (t Timestamp) Type() ref.Type {
return TimestampType
}
// Value implements ref.Val.Value.
func (t Timestamp) Value() any {
return t.Time
}
func (t Timestamp) format(sb *strings.Builder) {
fmt.Fprintf(sb, `timestamp("%s")`, t.Time.UTC().Format(time.RFC3339Nano))
}
var (
timestampValueType = reflect.TypeOf(&tpb.Timestamp{})
timestampZeroArgOverloads = map[string]func(time.Time) ref.Val{
overloads.TimeGetFullYear: timestampGetFullYear,
overloads.TimeGetMonth: timestampGetMonth,
overloads.TimeGetDayOfYear: timestampGetDayOfYear,
overloads.TimeGetDate: timestampGetDayOfMonthOneBased,
overloads.TimeGetDayOfMonth: timestampGetDayOfMonthZeroBased,
overloads.TimeGetDayOfWeek: timestampGetDayOfWeek,
overloads.TimeGetHours: timestampGetHours,
overloads.TimeGetMinutes: timestampGetMinutes,
overloads.TimeGetSeconds: timestampGetSeconds,
overloads.TimeGetMilliseconds: timestampGetMilliseconds}
timestampOneArgOverloads = map[string]func(time.Time, ref.Val) ref.Val{
overloads.TimeGetFullYear: timestampGetFullYearWithTz,
overloads.TimeGetMonth: timestampGetMonthWithTz,
overloads.TimeGetDayOfYear: timestampGetDayOfYearWithTz,
overloads.TimeGetDate: timestampGetDayOfMonthOneBasedWithTz,
overloads.TimeGetDayOfMonth: timestampGetDayOfMonthZeroBasedWithTz,
overloads.TimeGetDayOfWeek: timestampGetDayOfWeekWithTz,
overloads.TimeGetHours: timestampGetHoursWithTz,
overloads.TimeGetMinutes: timestampGetMinutesWithTz,
overloads.TimeGetSeconds: timestampGetSecondsWithTz,
overloads.TimeGetMilliseconds: timestampGetMillisecondsWithTz}
)
type timestampVisitor func(time.Time) ref.Val
func timestampGetFullYear(t time.Time) ref.Val {
return Int(t.Year())
}
func timestampGetMonth(t time.Time) ref.Val {
// CEL spec indicates that the month should be 0-based, but the Time value
// for Month() is 1-based.
return Int(t.Month() - 1)
}
func timestampGetDayOfYear(t time.Time) ref.Val {
return Int(t.YearDay() - 1)
}
func timestampGetDayOfMonthZeroBased(t time.Time) ref.Val {
return Int(t.Day() - 1)
}
func timestampGetDayOfMonthOneBased(t time.Time) ref.Val {
return Int(t.Day())
}
func timestampGetDayOfWeek(t time.Time) ref.Val {
return Int(t.Weekday())
}
func timestampGetHours(t time.Time) ref.Val {
return Int(t.Hour())
}
func timestampGetMinutes(t time.Time) ref.Val {
return Int(t.Minute())
}
func timestampGetSeconds(t time.Time) ref.Val {
return Int(t.Second())
}
func timestampGetMilliseconds(t time.Time) ref.Val {
return Int(t.Nanosecond() / 1000000)
}
func timestampGetFullYearWithTz(t time.Time, tz ref.Val) ref.Val {
return timeZone(tz, timestampGetFullYear)(t)
}
func timestampGetMonthWithTz(t time.Time, tz ref.Val) ref.Val {
return timeZone(tz, timestampGetMonth)(t)
}
func timestampGetDayOfYearWithTz(t time.Time, tz ref.Val) ref.Val {
return timeZone(tz, timestampGetDayOfYear)(t)
}
func timestampGetDayOfMonthZeroBasedWithTz(t time.Time, tz ref.Val) ref.Val {
return timeZone(tz, timestampGetDayOfMonthZeroBased)(t)
}
func timestampGetDayOfMonthOneBasedWithTz(t time.Time, tz ref.Val) ref.Val {
return timeZone(tz, timestampGetDayOfMonthOneBased)(t)
}
func timestampGetDayOfWeekWithTz(t time.Time, tz ref.Val) ref.Val {
return timeZone(tz, timestampGetDayOfWeek)(t)
}
func timestampGetHoursWithTz(t time.Time, tz ref.Val) ref.Val {
return timeZone(tz, timestampGetHours)(t)
}
func timestampGetMinutesWithTz(t time.Time, tz ref.Val) ref.Val {
return timeZone(tz, timestampGetMinutes)(t)
}
func timestampGetSecondsWithTz(t time.Time, tz ref.Val) ref.Val {
return timeZone(tz, timestampGetSeconds)(t)
}
func timestampGetMillisecondsWithTz(t time.Time, tz ref.Val) ref.Val {
return timeZone(tz, timestampGetMilliseconds)(t)
}
func timeZone(tz ref.Val, visitor timestampVisitor) timestampVisitor {
return func(t time.Time) ref.Val {
if StringType != tz.Type() {
return MaybeNoSuchOverloadErr(tz)
}
val := string(tz.(String))
ind := strings.Index(val, ":")
if ind == -1 {
loc, err := time.LoadLocation(val)
if err != nil {
return WrapErr(err)
}
return visitor(t.In(loc))
}
// If the input is not the name of a timezone (for example, 'US/Central'), it should be a numerical offset from UTC
// in the format ^(+|-)(0[0-9]|1[0-4]):[0-5][0-9]$. The numerical input is parsed in terms of hours and minutes.
hr, err := strconv.Atoi(string(val[0:ind]))
if err != nil {
return WrapErr(err)
}
min, err := strconv.Atoi(string(val[ind+1:]))
if err != nil {
return WrapErr(err)
}
var offset int
if string(val[0]) == "-" {
offset = hr*60 - min
} else {
offset = hr*60 + min
}
secondsEastOfUTC := int((time.Duration(offset) * time.Minute).Seconds())
timezone := time.FixedZone("", secondsEastOfUTC)
return visitor(t.In(timezone))
}
}
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package types
import (
"fmt"
"reflect"
"strings"
"google.golang.org/protobuf/proto"
chkdecls "github.com/google/cel-go/checker/decls"
"github.com/google/cel-go/common/types/ref"
"github.com/google/cel-go/common/types/traits"
celpb "cel.dev/expr"
exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1"
)
// Kind indicates a CEL type's kind which is used to differentiate quickly between simple
// and complex types.
type Kind uint
const (
// UnspecifiedKind is returned when the type is nil or its kind is not specified.
UnspecifiedKind Kind = iota
// DynKind represents a dynamic type. This kind only exists at type-check time.
DynKind
// AnyKind represents a google.protobuf.Any type. This kind only exists at type-check time.
// Prefer DynKind to AnyKind as AnyKind has a specific meaning which is based on protobuf
// well-known types.
AnyKind
// BoolKind represents a boolean type.
BoolKind
// BytesKind represents a bytes type.
BytesKind
// DoubleKind represents a double type.
DoubleKind
// DurationKind represents a CEL duration type.
DurationKind
// ErrorKind represents a CEL error type.
ErrorKind
// IntKind represents an integer type.
IntKind
// ListKind represents a list type.
ListKind
// MapKind represents a map type.
MapKind
// NullTypeKind represents a null type.
NullTypeKind
// OpaqueKind represents an abstract type which has no accessible fields.
OpaqueKind
// StringKind represents a string type.
StringKind
// StructKind represents a structured object with typed fields.
StructKind
// TimestampKind represents a a CEL time type.
TimestampKind
// TypeKind represents the CEL type.
TypeKind
// TypeParamKind represents a parameterized type whose type name will be resolved at type-check time, if possible.
TypeParamKind
// UintKind represents a uint type.
UintKind
// UnknownKind represents an unknown value type.
UnknownKind
)
var (
// AnyType represents the google.protobuf.Any type.
AnyType = &Type{
kind: AnyKind,
runtimeTypeName: "google.protobuf.Any",
traitMask: traits.FieldTesterType |
traits.IndexerType,
}
// BoolType represents the bool type.
BoolType = &Type{
kind: BoolKind,
runtimeTypeName: "bool",
traitMask: traits.ComparerType |
traits.NegatorType,
}
// BytesType represents the bytes type.
BytesType = &Type{
kind: BytesKind,
runtimeTypeName: "bytes",
traitMask: traits.AdderType |
traits.ComparerType |
traits.SizerType,
}
// DoubleType represents the double type.
DoubleType = &Type{
kind: DoubleKind,
runtimeTypeName: "double",
traitMask: traits.AdderType |
traits.ComparerType |
traits.DividerType |
traits.MultiplierType |
traits.NegatorType |
traits.SubtractorType,
}
// DurationType represents the CEL duration type.
DurationType = &Type{
kind: DurationKind,
runtimeTypeName: "google.protobuf.Duration",
traitMask: traits.AdderType |
traits.ComparerType |
traits.NegatorType |
traits.ReceiverType |
traits.SubtractorType,
}
// DynType represents a dynamic CEL type whose type will be determined at runtime from context.
DynType = &Type{
kind: DynKind,
runtimeTypeName: "dyn",
}
// ErrorType represents a CEL error value.
ErrorType = &Type{
kind: ErrorKind,
runtimeTypeName: "error",
}
// IntType represents the int type.
IntType = &Type{
kind: IntKind,
runtimeTypeName: "int",
traitMask: traits.AdderType |
traits.ComparerType |
traits.DividerType |
traits.ModderType |
traits.MultiplierType |
traits.NegatorType |
traits.SubtractorType,
}
// ListType represents the runtime list type.
ListType = NewListType(DynType)
// MapType represents the runtime map type.
MapType = NewMapType(DynType, DynType)
// NullType represents the type of a null value.
NullType = &Type{
kind: NullTypeKind,
runtimeTypeName: "null_type",
}
// StringType represents the string type.
StringType = &Type{
kind: StringKind,
runtimeTypeName: "string",
traitMask: traits.AdderType |
traits.ComparerType |
traits.MatcherType |
traits.ReceiverType |
traits.SizerType,
}
// TimestampType represents the time type.
TimestampType = &Type{
kind: TimestampKind,
runtimeTypeName: "google.protobuf.Timestamp",
traitMask: traits.AdderType |
traits.ComparerType |
traits.ReceiverType |
traits.SubtractorType,
}
// TypeType represents a CEL type
TypeType = &Type{
kind: TypeKind,
runtimeTypeName: "type",
}
// UintType represents a uint type.
UintType = &Type{
kind: UintKind,
runtimeTypeName: "uint",
traitMask: traits.AdderType |
traits.ComparerType |
traits.DividerType |
traits.ModderType |
traits.MultiplierType |
traits.SubtractorType,
}
// UnknownType represents an unknown value type.
UnknownType = &Type{
kind: UnknownKind,
runtimeTypeName: "unknown",
}
)
var _ ref.Type = &Type{}
var _ ref.Val = &Type{}
// Type holds a reference to a runtime type with an optional type-checked set of type parameters.
type Type struct {
// kind indicates general category of the type.
kind Kind
// parameters holds the optional type-checked set of type Parameters that are used during static analysis.
parameters []*Type
// runtimeTypeName indicates the runtime type name of the type.
runtimeTypeName string
// isAssignableType function determines whether one type is assignable to this type.
// A nil value for the isAssignableType function falls back to equality of kind, runtimeType, and parameters.
isAssignableType func(other *Type) bool
// isAssignableRuntimeType function determines whether the runtime type (with erasure) is assignable to this type.
// A nil value for the isAssignableRuntimeType function falls back to the equality of the type or type name.
isAssignableRuntimeType func(other ref.Val) bool
// traitMask is a mask of flags which indicate the capabilities of the type.
traitMask int
}
// ConvertToNative implements ref.Val.ConvertToNative.
func (t *Type) ConvertToNative(typeDesc reflect.Type) (any, error) {
return nil, fmt.Errorf("type conversion not supported for 'type'")
}
// ConvertToType implements ref.Val.ConvertToType.
func (t *Type) ConvertToType(typeVal ref.Type) ref.Val {
switch typeVal {
case TypeType:
return TypeType
case StringType:
return String(t.TypeName())
}
return NewErr("type conversion error from '%s' to '%s'", TypeType, typeVal)
}
// Equal indicates whether two types have the same runtime type name.
//
// The name Equal is a bit of a misnomer, but for historical reasons, this is the
// runtime behavior. For a more accurate definition see IsType().
func (t *Type) Equal(other ref.Val) ref.Val {
otherType, ok := other.(ref.Type)
return Bool(ok && t.TypeName() == otherType.TypeName())
}
// HasTrait implements the ref.Type interface method.
func (t *Type) HasTrait(trait int) bool {
return trait&t.traitMask == trait
}
// IsExactType indicates whether the two types are exactly the same. This check also verifies type parameter type names.
func (t *Type) IsExactType(other *Type) bool {
return t.isTypeInternal(other, true)
}
// IsEquivalentType indicates whether two types are equivalent. This check ignores type parameter type names.
func (t *Type) IsEquivalentType(other *Type) bool {
return t.isTypeInternal(other, false)
}
// Kind indicates general category of the type.
func (t *Type) Kind() Kind {
if t == nil {
return UnspecifiedKind
}
return t.kind
}
// isTypeInternal checks whether the two types are equivalent or exactly the same based on the checkTypeParamName flag.
func (t *Type) isTypeInternal(other *Type, checkTypeParamName bool) bool {
if t == nil {
return false
}
if t == other {
return true
}
if t.Kind() != other.Kind() || len(t.Parameters()) != len(other.Parameters()) {
return false
}
if (checkTypeParamName || t.Kind() != TypeParamKind) && t.TypeName() != other.TypeName() {
return false
}
for i, p := range t.Parameters() {
if !p.isTypeInternal(other.Parameters()[i], checkTypeParamName) {
return false
}
}
return true
}
// IsAssignableType determines whether the current type is type-check assignable from the input fromType.
func (t *Type) IsAssignableType(fromType *Type) bool {
if t == nil {
return false
}
if t.isAssignableType != nil {
return t.isAssignableType(fromType)
}
return t.defaultIsAssignableType(fromType)
}
// IsAssignableRuntimeType determines whether the current type is runtime assignable from the input runtimeType.
//
// At runtime, parameterized types are erased and so a function which type-checks to support a map(string, string)
// will have a runtime assignable type of a map.
func (t *Type) IsAssignableRuntimeType(val ref.Val) bool {
if t == nil {
return false
}
if t.isAssignableRuntimeType != nil {
return t.isAssignableRuntimeType(val)
}
return t.defaultIsAssignableRuntimeType(val)
}
// Parameters returns the list of type parameters if set.
//
// For ListKind, Parameters()[0] represents the list element type
// For MapKind, Parameters()[0] represents the map key type, and Parameters()[1] represents the map
// value type.
func (t *Type) Parameters() []*Type {
if t == nil {
return emptyParams
}
return t.parameters
}
// DeclaredTypeName indicates the fully qualified and parameterized type-check type name.
func (t *Type) DeclaredTypeName() string {
// if the type itself is neither null, nor dyn, but is assignable to null, then it's a wrapper type.
if t.Kind() != NullTypeKind && !t.isDyn() && t.IsAssignableType(NullType) {
return fmt.Sprintf("wrapper(%s)", t.TypeName())
}
return t.TypeName()
}
// Type implements the ref.Val interface method.
func (t *Type) Type() ref.Type {
return TypeType
}
// Value implements the ref.Val interface method.
func (t *Type) Value() any {
return t.TypeName()
}
// TypeName returns the type-erased fully qualified runtime type name.
//
// TypeName implements the ref.Type interface method.
func (t *Type) TypeName() string {
if t == nil {
return ""
}
return t.runtimeTypeName
}
func (t *Type) format(sb *strings.Builder) {
sb.WriteString(t.TypeName())
}
// WithTraits creates a copy of the current Type and sets the trait mask to the traits parameter.
//
// This method should be used with Opaque types where the type acts like a container, e.g. vector.
func (t *Type) WithTraits(traits int) *Type {
if t == nil {
return nil
}
return &Type{
kind: t.kind,
parameters: t.parameters,
runtimeTypeName: t.runtimeTypeName,
isAssignableType: t.isAssignableType,
isAssignableRuntimeType: t.isAssignableRuntimeType,
traitMask: traits,
}
}
// String returns a human-readable definition of the type name.
func (t *Type) String() string {
if t.Kind() == TypeParamKind {
return fmt.Sprintf("<%s>", t.DeclaredTypeName())
}
if len(t.Parameters()) == 0 {
return t.DeclaredTypeName()
}
params := make([]string, len(t.Parameters()))
for i, p := range t.Parameters() {
params[i] = p.String()
}
return fmt.Sprintf("%s(%s)", t.DeclaredTypeName(), strings.Join(params, ", "))
}
// isDyn indicates whether the type is dynamic in any way.
func (t *Type) isDyn() bool {
k := t.Kind()
return k == DynKind || k == AnyKind || k == TypeParamKind
}
// defaultIsAssignableType provides the standard definition of what it means for one type to be assignable to another
// where any of the following may return a true result:
// - The from types are the same instance
// - The target type is dynamic
// - The fromType has the same kind and type name as the target type, and all parameters of the target type
//
// are IsAssignableType() from the parameters of the fromType.
func (t *Type) defaultIsAssignableType(fromType *Type) bool {
if t == fromType || t.isDyn() {
return true
}
if t.Kind() != fromType.Kind() ||
t.TypeName() != fromType.TypeName() ||
len(t.Parameters()) != len(fromType.Parameters()) {
return false
}
for i, tp := range t.Parameters() {
fp := fromType.Parameters()[i]
if !tp.IsAssignableType(fp) {
return false
}
}
return true
}
// defaultIsAssignableRuntimeType inspects the type and in the case of list and map elements, the key and element types
// to determine whether a ref.Val is assignable to the declared type for a function signature.
func (t *Type) defaultIsAssignableRuntimeType(val ref.Val) bool {
valType := val.Type()
// If the current type and value type don't agree, then return
if !(t.isDyn() || t.TypeName() == valType.TypeName()) {
return false
}
switch t.Kind() {
case ListKind:
elemType := t.Parameters()[0]
l := val.(traits.Lister)
if l.Size() == IntZero {
return true
}
it := l.Iterator()
elemVal := it.Next()
return elemType.IsAssignableRuntimeType(elemVal)
case MapKind:
keyType := t.Parameters()[0]
elemType := t.Parameters()[1]
m := val.(traits.Mapper)
if m.Size() == IntZero {
return true
}
it := m.Iterator()
keyVal := it.Next()
elemVal := m.Get(keyVal)
return keyType.IsAssignableRuntimeType(keyVal) && elemType.IsAssignableRuntimeType(elemVal)
}
return true
}
// NewListType creates an instances of a list type value with the provided element type.
func NewListType(elemType *Type) *Type {
return &Type{
kind: ListKind,
parameters: []*Type{elemType},
runtimeTypeName: "list",
traitMask: traits.AdderType |
traits.ContainerType |
traits.IndexerType |
traits.IterableType |
traits.SizerType,
}
}
// NewMapType creates an instance of a map type value with the provided key and value types.
func NewMapType(keyType, valueType *Type) *Type {
return &Type{
kind: MapKind,
parameters: []*Type{keyType, valueType},
runtimeTypeName: "map",
traitMask: traits.ContainerType |
traits.IndexerType |
traits.IterableType |
traits.SizerType,
}
}
// NewNullableType creates an instance of a nullable type with the provided wrapped type.
//
// Note: only primitive types are supported as wrapped types.
func NewNullableType(wrapped *Type) *Type {
return &Type{
kind: wrapped.Kind(),
parameters: wrapped.Parameters(),
runtimeTypeName: wrapped.TypeName(),
traitMask: wrapped.traitMask,
isAssignableType: func(other *Type) bool {
return NullType.IsAssignableType(other) || wrapped.IsAssignableType(other)
},
isAssignableRuntimeType: func(other ref.Val) bool {
return NullType.IsAssignableRuntimeType(other) || wrapped.IsAssignableRuntimeType(other)
},
}
}
// NewOptionalType creates an abstract parameterized type instance corresponding to CEL's notion of optional.
func NewOptionalType(param *Type) *Type {
return NewOpaqueType("optional_type", param)
}
// NewOpaqueType creates an abstract parameterized type with a given name.
func NewOpaqueType(name string, params ...*Type) *Type {
return &Type{
kind: OpaqueKind,
parameters: params,
runtimeTypeName: name,
}
}
// NewObjectType creates a type reference to an externally defined type, e.g. a protobuf message type.
//
// An object type is assumed to support field presence testing and field indexing. Additionally, the
// type may also indicate additional traits through the use of the optional traits vararg argument.
func NewObjectType(typeName string, traits ...int) *Type {
// Function sanitizes object types on the fly
if wkt, found := checkedWellKnowns[typeName]; found {
return wkt
}
traitMask := 0
for _, trait := range traits {
traitMask |= trait
}
return &Type{
kind: StructKind,
parameters: emptyParams,
runtimeTypeName: typeName,
traitMask: structTypeTraitMask | traitMask,
}
}
// NewObjectTypeValue creates a type reference to an externally defined type.
//
// Deprecated: use cel.ObjectType(typeName)
func NewObjectTypeValue(typeName string) *Type {
return NewObjectType(typeName)
}
// NewTypeValue creates an opaque type which has a set of optional type traits as defined in
// the common/types/traits package.
//
// Deprecated: use cel.ObjectType(typeName, traits)
func NewTypeValue(typeName string, traits ...int) *Type {
traitMask := 0
for _, trait := range traits {
traitMask |= trait
}
return &Type{
kind: StructKind,
parameters: emptyParams,
runtimeTypeName: typeName,
traitMask: traitMask,
}
}
// NewTypeParamType creates a parameterized type instance.
func NewTypeParamType(paramName string) *Type {
return &Type{
kind: TypeParamKind,
runtimeTypeName: paramName,
}
}
// NewTypeTypeWithParam creates a type with a type parameter.
// Used for type-checking purposes, but equivalent to TypeType otherwise.
func NewTypeTypeWithParam(param *Type) *Type {
return &Type{
kind: TypeKind,
runtimeTypeName: "type",
parameters: []*Type{param},
}
}
// TypeToExprType converts a CEL-native type representation to a protobuf CEL Type representation.
func TypeToExprType(t *Type) (*exprpb.Type, error) {
switch t.Kind() {
case AnyKind:
return chkdecls.Any, nil
case BoolKind:
return maybeWrapper(t, chkdecls.Bool), nil
case BytesKind:
return maybeWrapper(t, chkdecls.Bytes), nil
case DoubleKind:
return maybeWrapper(t, chkdecls.Double), nil
case DurationKind:
return chkdecls.Duration, nil
case DynKind:
return chkdecls.Dyn, nil
case ErrorKind:
return chkdecls.Error, nil
case IntKind:
return maybeWrapper(t, chkdecls.Int), nil
case ListKind:
if len(t.Parameters()) != 1 {
return nil, fmt.Errorf("invalid list, got %d parameters, wanted one", len(t.Parameters()))
}
et, err := TypeToExprType(t.Parameters()[0])
if err != nil {
return nil, err
}
return chkdecls.NewListType(et), nil
case MapKind:
if len(t.Parameters()) != 2 {
return nil, fmt.Errorf("invalid map, got %d parameters, wanted two", len(t.Parameters()))
}
kt, err := TypeToExprType(t.Parameters()[0])
if err != nil {
return nil, err
}
vt, err := TypeToExprType(t.Parameters()[1])
if err != nil {
return nil, err
}
return chkdecls.NewMapType(kt, vt), nil
case NullTypeKind:
return chkdecls.Null, nil
case OpaqueKind:
params := make([]*exprpb.Type, len(t.Parameters()))
for i, p := range t.Parameters() {
pt, err := TypeToExprType(p)
if err != nil {
return nil, err
}
params[i] = pt
}
return chkdecls.NewAbstractType(t.TypeName(), params...), nil
case StringKind:
return maybeWrapper(t, chkdecls.String), nil
case StructKind:
return chkdecls.NewObjectType(t.TypeName()), nil
case TimestampKind:
return chkdecls.Timestamp, nil
case TypeParamKind:
return chkdecls.NewTypeParamType(t.TypeName()), nil
case TypeKind:
if len(t.Parameters()) == 1 {
p, err := TypeToExprType(t.Parameters()[0])
if err != nil {
return nil, err
}
return chkdecls.NewTypeType(p), nil
}
return chkdecls.NewTypeType(nil), nil
case UintKind:
return maybeWrapper(t, chkdecls.Uint), nil
}
return nil, fmt.Errorf("missing type conversion to proto: %v", t)
}
// ExprTypeToType converts a protobuf CEL type representation to a CEL-native type representation.
func ExprTypeToType(t *exprpb.Type) (*Type, error) {
return AlphaProtoAsType(t)
}
// AlphaProtoAsType converts a CEL v1alpha1.Type protobuf type to a CEL-native type representation.
func AlphaProtoAsType(t *exprpb.Type) (*Type, error) {
canonical := &celpb.Type{}
if err := convertProto(t, canonical); err != nil {
return nil, err
}
return ProtoAsType(canonical)
}
// ProtoAsType converts a canonical CEL celpb.Type protobuf type to a CEL-native type representation.
func ProtoAsType(t *celpb.Type) (*Type, error) {
switch t.GetTypeKind().(type) {
case *celpb.Type_Dyn:
return DynType, nil
case *celpb.Type_AbstractType_:
paramTypes := make([]*Type, len(t.GetAbstractType().GetParameterTypes()))
for i, p := range t.GetAbstractType().GetParameterTypes() {
pt, err := ProtoAsType(p)
if err != nil {
return nil, err
}
paramTypes[i] = pt
}
return NewOpaqueType(t.GetAbstractType().GetName(), paramTypes...), nil
case *celpb.Type_ListType_:
et, err := ProtoAsType(t.GetListType().GetElemType())
if err != nil {
return nil, err
}
return NewListType(et), nil
case *celpb.Type_MapType_:
kt, err := ProtoAsType(t.GetMapType().GetKeyType())
if err != nil {
return nil, err
}
vt, err := ProtoAsType(t.GetMapType().GetValueType())
if err != nil {
return nil, err
}
return NewMapType(kt, vt), nil
case *celpb.Type_MessageType:
return NewObjectType(t.GetMessageType()), nil
case *celpb.Type_Null:
return NullType, nil
case *celpb.Type_Primitive:
switch t.GetPrimitive() {
case celpb.Type_BOOL:
return BoolType, nil
case celpb.Type_BYTES:
return BytesType, nil
case celpb.Type_DOUBLE:
return DoubleType, nil
case celpb.Type_INT64:
return IntType, nil
case celpb.Type_STRING:
return StringType, nil
case celpb.Type_UINT64:
return UintType, nil
default:
return nil, fmt.Errorf("unsupported primitive type: %v", t)
}
case *celpb.Type_TypeParam:
return NewTypeParamType(t.GetTypeParam()), nil
case *celpb.Type_Type:
if t.GetType().GetTypeKind() != nil {
p, err := ProtoAsType(t.GetType())
if err != nil {
return nil, err
}
return NewTypeTypeWithParam(p), nil
}
return TypeType, nil
case *celpb.Type_WellKnown:
switch t.GetWellKnown() {
case celpb.Type_ANY:
return AnyType, nil
case celpb.Type_DURATION:
return DurationType, nil
case celpb.Type_TIMESTAMP:
return TimestampType, nil
default:
return nil, fmt.Errorf("unsupported well-known type: %v", t)
}
case *celpb.Type_Wrapper:
t, err := ProtoAsType(&celpb.Type{TypeKind: &celpb.Type_Primitive{Primitive: t.GetWrapper()}})
if err != nil {
return nil, err
}
return NewNullableType(t), nil
case *celpb.Type_Error:
return ErrorType, nil
default:
return nil, fmt.Errorf("unsupported type: %v", t)
}
}
// TypeToProto converts from a CEL-native type representation to canonical CEL celpb.Type protobuf type.
func TypeToProto(t *Type) (*celpb.Type, error) {
exprType, err := TypeToExprType(t)
if err != nil {
return nil, err
}
var pbtype celpb.Type
if err = convertProto(exprType, &pbtype); err != nil {
return nil, err
}
return &pbtype, nil
}
func maybeWrapper(t *Type, pbType *exprpb.Type) *exprpb.Type {
if t.IsAssignableType(NullType) {
return chkdecls.NewWrapperType(pbType)
}
return pbType
}
func maybeForeignType(t ref.Type) *Type {
if celType, ok := t.(*Type); ok {
return celType
}
// Inspect the incoming type to determine its traits. The assumption will be that the incoming
// type does not have any field values; however, if the trait mask indicates that field testing
// and indexing are supported, the foreign type is marked as a struct.
traitMask := 0
for _, trait := range allTraits {
if t.HasTrait(trait) {
traitMask |= trait
}
}
// Treat the value like a struct. If it has no fields, this is harmless to denote the type
// as such since it basically becomes an opaque type by convention.
return NewObjectType(t.TypeName(), traitMask)
}
func convertProto(src, dst proto.Message) error {
pb, err := proto.Marshal(src)
if err != nil {
return err
}
err = proto.Unmarshal(pb, dst)
return err
}
func primitiveType(primitive celpb.Type_PrimitiveType) *celpb.Type {
return &celpb.Type{
TypeKind: &celpb.Type_Primitive{
Primitive: primitive,
},
}
}
var (
checkedWellKnowns = map[string]*Type{
// Wrapper types.
"google.protobuf.BoolValue": NewNullableType(BoolType),
"google.protobuf.BytesValue": NewNullableType(BytesType),
"google.protobuf.DoubleValue": NewNullableType(DoubleType),
"google.protobuf.FloatValue": NewNullableType(DoubleType),
"google.protobuf.Int64Value": NewNullableType(IntType),
"google.protobuf.Int32Value": NewNullableType(IntType),
"google.protobuf.UInt64Value": NewNullableType(UintType),
"google.protobuf.UInt32Value": NewNullableType(UintType),
"google.protobuf.StringValue": NewNullableType(StringType),
// Well-known types.
"google.protobuf.Any": AnyType,
"google.protobuf.Duration": DurationType,
"google.protobuf.Timestamp": TimestampType,
// Json types.
"google.protobuf.ListValue": NewListType(DynType),
"google.protobuf.NullValue": NullType,
"google.protobuf.Struct": NewMapType(StringType, DynType),
"google.protobuf.Value": DynType,
}
emptyParams = []*Type{}
allTraits = []int{
traits.AdderType,
traits.ComparerType,
traits.ContainerType,
traits.DividerType,
traits.FieldTesterType,
traits.IndexerType,
traits.IterableType,
traits.IteratorType,
traits.MatcherType,
traits.ModderType,
traits.MultiplierType,
traits.NegatorType,
traits.ReceiverType,
traits.SizerType,
traits.SubtractorType,
}
structTypeTraitMask = traits.FieldTesterType | traits.IndexerType
boolType = primitiveType(celpb.Type_BOOL)
bytesType = primitiveType(celpb.Type_BYTES)
doubleType = primitiveType(celpb.Type_DOUBLE)
intType = primitiveType(celpb.Type_INT64)
stringType = primitiveType(celpb.Type_STRING)
uintType = primitiveType(celpb.Type_UINT64)
)
// Copyright 2018 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package types
import (
"fmt"
"math"
"reflect"
"strconv"
"strings"
"github.com/google/cel-go/common/types/ref"
anypb "google.golang.org/protobuf/types/known/anypb"
structpb "google.golang.org/protobuf/types/known/structpb"
wrapperspb "google.golang.org/protobuf/types/known/wrapperspb"
)
// Uint type implementation which supports comparison and math operators.
type Uint uint64
var (
uint32WrapperType = reflect.TypeOf(&wrapperspb.UInt32Value{})
uint64WrapperType = reflect.TypeOf(&wrapperspb.UInt64Value{})
)
// Uint constants
const (
uintZero = Uint(0)
)
// Add implements traits.Adder.Add.
func (i Uint) Add(other ref.Val) ref.Val {
otherUint, ok := other.(Uint)
if !ok {
return MaybeNoSuchOverloadErr(other)
}
val, err := addUint64Checked(uint64(i), uint64(otherUint))
if err != nil {
return WrapErr(err)
}
return Uint(val)
}
// Compare implements traits.Comparer.Compare.
func (i Uint) Compare(other ref.Val) ref.Val {
switch ov := other.(type) {
case Double:
if math.IsNaN(float64(ov)) {
return NewErr("NaN values cannot be ordered")
}
return compareUintDouble(i, ov)
case Int:
return compareUintInt(i, ov)
case Uint:
return compareUint(i, ov)
default:
return MaybeNoSuchOverloadErr(other)
}
}
// ConvertToNative implements ref.Val.ConvertToNative.
func (i Uint) ConvertToNative(typeDesc reflect.Type) (any, error) {
switch typeDesc.Kind() {
case reflect.Uint, reflect.Uint32:
v, err := uint64ToUint32Checked(uint64(i))
if err != nil {
return 0, err
}
return reflect.ValueOf(v).Convert(typeDesc).Interface(), nil
case reflect.Uint8:
v, err := uint64ToUint8Checked(uint64(i))
if err != nil {
return 0, err
}
return reflect.ValueOf(v).Convert(typeDesc).Interface(), nil
case reflect.Uint16:
v, err := uint64ToUint16Checked(uint64(i))
if err != nil {
return 0, err
}
return reflect.ValueOf(v).Convert(typeDesc).Interface(), nil
case reflect.Uint64:
return reflect.ValueOf(i).Convert(typeDesc).Interface(), nil
case reflect.Ptr:
switch typeDesc {
case anyValueType:
// Primitives must be wrapped before being set on an Any field.
return anypb.New(wrapperspb.UInt64(uint64(i)))
case jsonValueType:
// JSON can accurately represent 32-bit uints as floating point values.
if i.isJSONSafe() {
return structpb.NewNumberValue(float64(i)), nil
}
// Proto3 to JSON conversion requires string-formatted uint64 values
// since the conversion to floating point would result in truncation.
return structpb.NewStringValue(strconv.FormatUint(uint64(i), 10)), nil
case uint32WrapperType:
// Convert the value to a wrapperspb.UInt32Value, error on overflow.
v, err := uint64ToUint32Checked(uint64(i))
if err != nil {
return 0, err
}
return wrapperspb.UInt32(v), nil
case uint64WrapperType:
// Convert the value to a wrapperspb.UInt64Value.
return wrapperspb.UInt64(uint64(i)), nil
}
switch typeDesc.Elem().Kind() {
case reflect.Uint32:
v, err := uint64ToUint32Checked(uint64(i))
if err != nil {
return 0, err
}
p := reflect.New(typeDesc.Elem())
p.Elem().Set(reflect.ValueOf(v).Convert(typeDesc.Elem()))
return p.Interface(), nil
case reflect.Uint64:
v := uint64(i)
p := reflect.New(typeDesc.Elem())
p.Elem().Set(reflect.ValueOf(v).Convert(typeDesc.Elem()))
return p.Interface(), nil
}
case reflect.Interface:
iv := i.Value()
if reflect.TypeOf(iv).Implements(typeDesc) {
return iv, nil
}
if reflect.TypeOf(i).Implements(typeDesc) {
return i, nil
}
}
return nil, fmt.Errorf("unsupported type conversion from 'uint' to %v", typeDesc)
}
// ConvertToType implements ref.Val.ConvertToType.
func (i Uint) ConvertToType(typeVal ref.Type) ref.Val {
switch typeVal {
case IntType:
v, err := uint64ToInt64Checked(uint64(i))
if err != nil {
return WrapErr(err)
}
return Int(v)
case UintType:
return i
case DoubleType:
return Double(i)
case StringType:
return String(fmt.Sprintf("%d", uint64(i)))
case TypeType:
return UintType
}
return NewErr("type conversion error from '%s' to '%s'", UintType, typeVal)
}
// Divide implements traits.Divider.Divide.
func (i Uint) Divide(other ref.Val) ref.Val {
otherUint, ok := other.(Uint)
if !ok {
return MaybeNoSuchOverloadErr(other)
}
div, err := divideUint64Checked(uint64(i), uint64(otherUint))
if err != nil {
return WrapErr(err)
}
return Uint(div)
}
// Equal implements ref.Val.Equal.
func (i Uint) Equal(other ref.Val) ref.Val {
switch ov := other.(type) {
case Double:
if math.IsNaN(float64(ov)) {
return False
}
return Bool(compareUintDouble(i, ov) == 0)
case Int:
return Bool(compareUintInt(i, ov) == 0)
case Uint:
return Bool(i == ov)
default:
return False
}
}
// IsZeroValue returns true if the uint is zero.
func (i Uint) IsZeroValue() bool {
return i == 0
}
// Modulo implements traits.Modder.Modulo.
func (i Uint) Modulo(other ref.Val) ref.Val {
otherUint, ok := other.(Uint)
if !ok {
return MaybeNoSuchOverloadErr(other)
}
mod, err := moduloUint64Checked(uint64(i), uint64(otherUint))
if err != nil {
return WrapErr(err)
}
return Uint(mod)
}
// Multiply implements traits.Multiplier.Multiply.
func (i Uint) Multiply(other ref.Val) ref.Val {
otherUint, ok := other.(Uint)
if !ok {
return MaybeNoSuchOverloadErr(other)
}
val, err := multiplyUint64Checked(uint64(i), uint64(otherUint))
if err != nil {
return WrapErr(err)
}
return Uint(val)
}
// Subtract implements traits.Subtractor.Subtract.
func (i Uint) Subtract(subtrahend ref.Val) ref.Val {
subtraUint, ok := subtrahend.(Uint)
if !ok {
return MaybeNoSuchOverloadErr(subtrahend)
}
val, err := subtractUint64Checked(uint64(i), uint64(subtraUint))
if err != nil {
return WrapErr(err)
}
return Uint(val)
}
// Type implements ref.Val.Type.
func (i Uint) Type() ref.Type {
return UintType
}
// Value implements ref.Val.Value.
func (i Uint) Value() any {
return uint64(i)
}
func (i Uint) format(sb *strings.Builder) {
sb.WriteString(strconv.FormatUint(uint64(i), 10))
sb.WriteString("u")
}
// isJSONSafe indicates whether the uint is safely representable as a floating point value in JSON.
func (i Uint) isJSONSafe() bool {
return i <= maxIntJSON
}
// Copyright 2018 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package types
import (
"fmt"
"math"
"reflect"
"sort"
"strings"
"unicode"
"github.com/google/cel-go/common/types/ref"
)
var (
unspecifiedAttribute = &AttributeTrail{qualifierPath: []any{}}
)
// NewAttributeTrail creates a new simple attribute from a variable name.
func NewAttributeTrail(variable string) *AttributeTrail {
if variable == "" {
return unspecifiedAttribute
}
return &AttributeTrail{variable: variable}
}
// AttributeTrail specifies a variable with an optional qualifier path. An attribute value is expected to
// correspond to an AbsoluteAttribute, meaning a field selection which starts with a top-level variable.
//
// The qualifer path elements adhere to the AttributeQualifier type constraint.
type AttributeTrail struct {
variable string
qualifierPath []any
}
// Equal returns whether two attribute values have the same variable name and qualifier paths.
func (a *AttributeTrail) Equal(other *AttributeTrail) bool {
if a.Variable() != other.Variable() || len(a.QualifierPath()) != len(other.QualifierPath()) {
return false
}
for i, q := range a.QualifierPath() {
qual := other.QualifierPath()[i]
if !qualifiersEqual(q, qual) {
return false
}
}
return true
}
func qualifiersEqual(a, b any) bool {
if a == b {
return true
}
switch numA := a.(type) {
case int64:
numB, ok := b.(uint64)
if !ok {
return false
}
return intUintEqual(numA, numB)
case uint64:
numB, ok := b.(int64)
if !ok {
return false
}
return intUintEqual(numB, numA)
default:
return false
}
}
func intUintEqual(i int64, u uint64) bool {
if i < 0 || u > math.MaxInt64 {
return false
}
return i == int64(u)
}
// Variable returns the variable name associated with the attribute.
func (a *AttributeTrail) Variable() string {
return a.variable
}
// QualifierPath returns the optional set of qualifying fields or indices applied to the variable.
func (a *AttributeTrail) QualifierPath() []any {
return a.qualifierPath
}
// String returns the string representation of the Attribute.
func (a *AttributeTrail) String() string {
if a.variable == "" {
return "<unspecified>"
}
var str strings.Builder
str.WriteString(a.variable)
for _, q := range a.qualifierPath {
switch q := q.(type) {
case bool, int64:
str.WriteString(fmt.Sprintf("[%v]", q))
case uint64:
str.WriteString(fmt.Sprintf("[%vu]", q))
case string:
if isIdentifierCharacter(q) {
str.WriteString(fmt.Sprintf(".%v", q))
} else {
str.WriteString(fmt.Sprintf("[%q]", q))
}
}
}
return str.String()
}
func isIdentifierCharacter(str string) bool {
for _, c := range str {
if unicode.IsLetter(c) || unicode.IsDigit(c) || string(c) == "_" {
continue
}
return false
}
return true
}
// AttributeQualifier constrains the possible types which may be used to qualify an attribute.
type AttributeQualifier interface {
bool | int64 | uint64 | string
}
// QualifyAttribute qualifies an attribute using a valid AttributeQualifier type.
func QualifyAttribute[T AttributeQualifier](attr *AttributeTrail, qualifier T) *AttributeTrail {
attr.qualifierPath = append(attr.qualifierPath, qualifier)
return attr
}
// Unknown type which collects expression ids which caused the current value to become unknown.
type Unknown struct {
attributeTrails map[int64][]*AttributeTrail
}
// NewUnknown creates a new unknown at a given expression id for an attribute.
//
// If the attribute is nil, the attribute value will be the `unspecifiedAttribute`.
func NewUnknown(id int64, attr *AttributeTrail) *Unknown {
if attr == nil {
attr = unspecifiedAttribute
}
return &Unknown{
attributeTrails: map[int64][]*AttributeTrail{id: {attr}},
}
}
// IDs returns the set of unknown expression ids contained by this value.
//
// Numeric identifiers are guaranteed to be in sorted order.
func (u *Unknown) IDs() []int64 {
ids := make(int64Slice, len(u.attributeTrails))
i := 0
for id := range u.attributeTrails {
ids[i] = id
i++
}
ids.Sort()
return ids
}
// GetAttributeTrails returns the attribute trails, if present, missing for a given expression id.
func (u *Unknown) GetAttributeTrails(id int64) ([]*AttributeTrail, bool) {
trails, found := u.attributeTrails[id]
return trails, found
}
// Contains returns true if the input unknown is a subset of the current unknown.
func (u *Unknown) Contains(other *Unknown) bool {
for id, otherTrails := range other.attributeTrails {
trails, found := u.attributeTrails[id]
if !found || len(otherTrails) != len(trails) {
return false
}
for _, ot := range otherTrails {
found := false
for _, t := range trails {
if t.Equal(ot) {
found = true
break
}
}
if !found {
return false
}
}
}
return true
}
// ConvertToNative implements ref.Val.ConvertToNative.
func (u *Unknown) ConvertToNative(typeDesc reflect.Type) (any, error) {
return u.Value(), nil
}
// ConvertToType is an identity function since unknown values cannot be modified.
func (u *Unknown) ConvertToType(typeVal ref.Type) ref.Val {
return u
}
// Equal is an identity function since unknown values cannot be modified.
func (u *Unknown) Equal(other ref.Val) ref.Val {
return u
}
// String implements the Stringer interface
func (u *Unknown) String() string {
var str strings.Builder
for id, attrs := range u.attributeTrails {
if str.Len() != 0 {
str.WriteString(", ")
}
if len(attrs) == 1 {
str.WriteString(fmt.Sprintf("%v (%d)", attrs[0], id))
} else {
str.WriteString(fmt.Sprintf("%v (%d)", attrs, id))
}
}
return str.String()
}
// Type implements ref.Val.Type.
func (u *Unknown) Type() ref.Type {
return UnknownType
}
// Value implements ref.Val.Value.
func (u *Unknown) Value() any {
return u
}
// IsUnknown returns whether the element ref.Val is in instance of *types.Unknown
func IsUnknown(val ref.Val) bool {
switch val.(type) {
case *Unknown:
return true
default:
return false
}
}
// MaybeMergeUnknowns determines whether an input value and another, possibly nil, unknown will produce
// an unknown result.
//
// If the input `val` is another Unknown, then the result will be the merge of the `val` and the input
// `unk`. If the `val` is not unknown, then the result will depend on whether the input `unk` is nil.
// If both values are non-nil and unknown, then the return value will be a merge of both unknowns.
func MaybeMergeUnknowns(val ref.Val, unk *Unknown) (*Unknown, bool) {
src, isUnk := val.(*Unknown)
if !isUnk {
if unk != nil {
return unk, true
}
return unk, false
}
return MergeUnknowns(src, unk), true
}
// MergeUnknowns combines two unknown values into a new unknown value.
func MergeUnknowns(unk1, unk2 *Unknown) *Unknown {
if unk1 == nil {
return unk2
}
if unk2 == nil {
return unk1
}
out := &Unknown{
attributeTrails: make(map[int64][]*AttributeTrail, len(unk1.attributeTrails)+len(unk2.attributeTrails)),
}
for id, ats := range unk1.attributeTrails {
out.attributeTrails[id] = ats
}
for id, ats := range unk2.attributeTrails {
existing, found := out.attributeTrails[id]
if !found {
out.attributeTrails[id] = ats
continue
}
for _, at := range ats {
found := false
for _, et := range existing {
if at.Equal(et) {
found = true
break
}
}
if !found {
existing = append(existing, at)
}
}
out.attributeTrails[id] = existing
}
return out
}
// int64Slice is an implementation of the sort.Interface
type int64Slice []int64
// Len returns the number of elements in the slice.
func (x int64Slice) Len() int { return len(x) }
// Less indicates whether the value at index i is less than the value at index j.
func (x int64Slice) Less(i, j int) bool { return x[i] < x[j] }
// Swap swaps the values at indices i and j in place.
func (x int64Slice) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
// Sort is a convenience method: x.Sort() calls Sort(x).
func (x int64Slice) Sort() { sort.Sort(x) }
// Copyright 2018 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package types
import (
"github.com/google/cel-go/common/types/ref"
)
// IsUnknownOrError returns whether the input element ref.Val is an ErrType or UnknownType.
func IsUnknownOrError(val ref.Val) bool {
switch val.(type) {
case *Unknown, *Err:
return true
}
return false
}
// IsPrimitiveType returns whether the input element ref.Val is a primitive type.
// Note, primitive types do not include well-known types such as Duration and Timestamp.
func IsPrimitiveType(val ref.Val) bool {
switch val.Type() {
case BoolType, BytesType, DoubleType, IntType, StringType, UintType:
return true
}
return false
}
// Equal returns whether the two ref.Value are heterogeneously equivalent.
func Equal(lhs ref.Val, rhs ref.Val) ref.Val {
lNull := lhs == NullValue
rNull := rhs == NullValue
if lNull || rNull {
return Bool(lNull == rNull)
}
return lhs.Equal(rhs)
}
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package ext
import (
"errors"
"fmt"
"math"
"strconv"
"strings"
"sync"
"github.com/google/cel-go/cel"
"github.com/google/cel-go/common/ast"
"github.com/google/cel-go/common/types"
"github.com/google/cel-go/common/types/ref"
"github.com/google/cel-go/common/types/traits"
"github.com/google/cel-go/interpreter"
)
// Bindings returns a cel.EnvOption to configure support for local variable
// bindings in expressions.
//
// # Cel.Bind
//
// Binds a simple identifier to an initialization expression which may be used
// in a subsequenct result expression. Bindings may also be nested within each
// other.
//
// cel.bind(<varName>, <initExpr>, <resultExpr>)
//
// Examples:
//
// cel.bind(a, 'hello',
// cel.bind(b, 'world', a + b + b + a)) // "helloworldworldhello"
//
// // Avoid a list allocation within the exists comprehension.
// cel.bind(valid_values, [a, b, c],
// [d, e, f].exists(elem, elem in valid_values))
//
// Local bindings are not guaranteed to be evaluated before use.
func Bindings(options ...BindingsOption) cel.EnvOption {
b := &celBindings{version: math.MaxUint32}
for _, o := range options {
b = o(b)
}
return cel.Lib(b)
}
const (
celNamespace = "cel"
bindMacro = "bind"
blockFunc = "@block"
unusedIterVar = "#unused"
)
// BindingsOption declares a functional operator for configuring the Bindings library behavior.
type BindingsOption func(*celBindings) *celBindings
// BindingsVersion sets the version of the bindings library to an explicit version.
func BindingsVersion(version uint32) BindingsOption {
return func(lib *celBindings) *celBindings {
lib.version = version
return lib
}
}
type celBindings struct {
version uint32
}
func (*celBindings) LibraryName() string {
return "cel.lib.ext.cel.bindings"
}
func (lib *celBindings) CompileOptions() []cel.EnvOption {
opts := []cel.EnvOption{
cel.Macros(
// cel.bind(var, <init>, <expr>)
cel.ReceiverMacro(bindMacro, 3, celBind),
),
}
if lib.version >= 1 {
// The cel.@block signature takes a list of subexpressions and a typed expression which is
// used as the output type.
paramType := cel.TypeParamType("T")
opts = append(opts,
cel.Function("cel.@block",
cel.Overload("cel_block_list",
[]*cel.Type{cel.ListType(cel.DynType), paramType}, paramType)),
)
opts = append(opts, cel.ASTValidators(blockValidationExemption{}))
}
return opts
}
func (lib *celBindings) ProgramOptions() []cel.ProgramOption {
if lib.version >= 1 {
celBlockPlan := func(i interpreter.Interpretable) (interpreter.Interpretable, error) {
call, ok := i.(interpreter.InterpretableCall)
if !ok {
return i, nil
}
switch call.Function() {
case "cel.@block":
args := call.Args()
if len(args) != 2 {
return nil, fmt.Errorf("cel.@block expects two arguments, but got %d", len(args))
}
expr := args[1]
// Non-empty block
if block, ok := args[0].(interpreter.InterpretableConstructor); ok {
slotExprs := block.InitVals()
return newDynamicBlock(slotExprs, expr), nil
}
// Constant valued block which can happen during runtime optimization.
if cons, ok := args[0].(interpreter.InterpretableConst); ok {
if cons.Value().Type() == types.ListType {
l := cons.Value().(traits.Lister)
if l.Size().Equal(types.IntZero) == types.True {
return args[1], nil
}
return newConstantBlock(l, expr), nil
}
}
return nil, errors.New("cel.@block expects a list constructor as the first argument")
default:
return i, nil
}
}
return []cel.ProgramOption{cel.CustomDecorator(celBlockPlan)}
}
return []cel.ProgramOption{}
}
type blockValidationExemption struct{}
// Name returns the name of the validator.
func (blockValidationExemption) Name() string {
return "cel.validator.cel_block"
}
// Configure implements the ASTValidatorConfigurer interface and augments the list of functions to skip
// during homogeneous aggregate literal type-checks.
func (blockValidationExemption) Configure(config cel.MutableValidatorConfig) error {
functions := config.GetOrDefault(cel.HomogeneousAggregateLiteralExemptFunctions, []string{}).([]string)
functions = append(functions, "cel.@block")
return config.Set(cel.HomogeneousAggregateLiteralExemptFunctions, functions)
}
// Validate is a no-op as the intent is to simply disable strong type-checks for list literals during
// when they occur within cel.@block calls as the arg types have already been validated.
func (blockValidationExemption) Validate(env *cel.Env, _ cel.ValidatorConfig, a *ast.AST, iss *cel.Issues) {
}
func celBind(mef cel.MacroExprFactory, target ast.Expr, args []ast.Expr) (ast.Expr, *cel.Error) {
if !macroTargetMatchesNamespace(celNamespace, target) {
return nil, nil
}
varIdent := args[0]
varName := ""
switch varIdent.Kind() {
case ast.IdentKind:
varName = varIdent.AsIdent()
default:
return nil, mef.NewError(varIdent.ID(), "cel.bind() variable names must be simple identifiers")
}
varInit := args[1]
resultExpr := args[2]
return mef.NewComprehension(
mef.NewList(),
unusedIterVar,
varName,
varInit,
mef.NewLiteral(types.False),
mef.NewIdent(varName),
resultExpr,
), nil
}
func newDynamicBlock(slotExprs []interpreter.Interpretable, expr interpreter.Interpretable) interpreter.Interpretable {
bs := &dynamicBlock{
slotExprs: slotExprs,
expr: expr,
}
bs.slotActivationPool = &sync.Pool{
New: func() any {
slotCount := len(slotExprs)
sa := &dynamicSlotActivation{
slotExprs: slotExprs,
slotCount: slotCount,
slotVals: make([]*slotVal, slotCount),
}
for i := 0; i < slotCount; i++ {
sa.slotVals[i] = &slotVal{}
}
return sa
},
}
return bs
}
type dynamicBlock struct {
slotExprs []interpreter.Interpretable
expr interpreter.Interpretable
slotActivationPool *sync.Pool
}
// ID implements the Interpretable interface method.
func (b *dynamicBlock) ID() int64 {
return b.expr.ID()
}
// Eval implements the Interpretable interface method.
func (b *dynamicBlock) Eval(activation cel.Activation) ref.Val {
sa := b.slotActivationPool.Get().(*dynamicSlotActivation)
sa.Activation = activation
defer b.clearSlots(sa)
return b.expr.Eval(sa)
}
func (b *dynamicBlock) clearSlots(sa *dynamicSlotActivation) {
sa.reset()
b.slotActivationPool.Put(sa)
}
type slotVal struct {
value *ref.Val
visited bool
}
type dynamicSlotActivation struct {
cel.Activation
slotExprs []interpreter.Interpretable
slotCount int
slotVals []*slotVal
}
// ResolveName implements the Activation interface method but handles variables prefixed with `@index`
// as special variables which exist within the slot-based memory of the cel.@block() where each slot
// refers to an expression which must be computed only once.
func (sa *dynamicSlotActivation) ResolveName(name string) (any, bool) {
if idx, found := matchSlot(name, sa.slotCount); found {
v := sa.slotVals[idx]
if v.visited {
// Return not found if the index expression refers to itself
if v.value == nil {
return nil, false
}
return *v.value, true
}
v.visited = true
val := sa.slotExprs[idx].Eval(sa)
v.value = &val
return val, true
}
return sa.Activation.ResolveName(name)
}
func (sa *dynamicSlotActivation) reset() {
sa.Activation = nil
for _, sv := range sa.slotVals {
sv.visited = false
sv.value = nil
}
}
func newConstantBlock(slots traits.Lister, expr interpreter.Interpretable) interpreter.Interpretable {
count := slots.Size().(types.Int)
return &constantBlock{slots: slots, slotCount: int(count), expr: expr}
}
type constantBlock struct {
slots traits.Lister
slotCount int
expr interpreter.Interpretable
}
// ID implements the interpreter.Interpretable interface method.
func (b *constantBlock) ID() int64 {
return b.expr.ID()
}
// Eval implements the interpreter.Interpretable interface method, and will proxy @index prefixed variable
// lookups into a set of constant slots determined from the plan step.
func (b *constantBlock) Eval(activation cel.Activation) ref.Val {
vars := constantSlotActivation{Activation: activation, slots: b.slots, slotCount: b.slotCount}
return b.expr.Eval(vars)
}
type constantSlotActivation struct {
cel.Activation
slots traits.Lister
slotCount int
}
// ResolveName implements Activation interface method and proxies @index prefixed lookups into the slot
// activation associated with the block scope.
func (sa constantSlotActivation) ResolveName(name string) (any, bool) {
if idx, found := matchSlot(name, sa.slotCount); found {
return sa.slots.Get(types.Int(idx)), true
}
return sa.Activation.ResolveName(name)
}
func matchSlot(name string, slotCount int) (int, bool) {
if idx, found := strings.CutPrefix(name, indexPrefix); found {
idx, err := strconv.Atoi(idx)
// Return not found if the index is not numeric
if err != nil {
return -1, false
}
// Return not found if the index is not a valid slot
if idx < 0 || idx >= slotCount {
return -1, false
}
return idx, true
}
return -1, false
}
var (
indexPrefix = "@index"
)
// Copyright 2024 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package ext
import (
"fmt"
"math"
"github.com/google/cel-go/cel"
"github.com/google/cel-go/common/ast"
"github.com/google/cel-go/common/operators"
"github.com/google/cel-go/common/types"
"github.com/google/cel-go/common/types/ref"
"github.com/google/cel-go/common/types/traits"
"github.com/google/cel-go/parser"
)
const (
mapInsert = "cel.@mapInsert"
mapInsertOverloadMap = "@mapInsert_map_map"
mapInsertOverloadKeyValue = "@mapInsert_map_key_value"
)
// TwoVarComprehensions introduces support for two-variable comprehensions.
//
// The two-variable form of comprehensions looks similar to the one-variable counterparts.
// Where possible, the same macro names were used and additional macro signatures added.
// The notable distinction for two-variable comprehensions is the introduction of
// `transformList`, `transformMap`, and `transformMapEntry` support for list and map types
// rather than the more traditional `map` and `filter` macros.
//
// # All
//
// Comprehension which tests whether all elements in the list or map satisfy a given
// predicate. The `all` macro evaluates in a manner consistent with logical AND and will
// short-circuit when encountering a `false` value.
//
// <list>.all(indexVar, valueVar, <predicate>) -> bool
// <map>.all(keyVar, valueVar, <predicate>) -> bool
//
// Examples:
//
// [1, 2, 3].all(i, j, i < j) // returns true
// {'hello': 'world', 'taco': 'taco'}.all(k, v, k != v) // returns false
//
// // Combines two-variable comprehension with single variable
// {'h': ['hello', 'hi'], 'j': ['joke', 'jog']}
// .all(k, vals, vals.all(v, v.startsWith(k))) // returns true
//
// # Exists
//
// Comprehension which tests whether any element in a list or map exists which satisfies
// a given predicate. The `exists` macro evaluates in a manner consistent with logical OR
// and will short-circuit when encountering a `true` value.
//
// <list>.exists(indexVar, valueVar, <predicate>) -> bool
// <map>.exists(keyVar, valueVar, <predicate>) -> bool
//
// Examples:
//
// {'greeting': 'hello', 'farewell': 'goodbye'}
// .exists(k, v, k.startsWith('good') || v.endsWith('bye')) // returns true
// [1, 2, 4, 8, 16].exists(i, v, v == 1024 && i == 10) // returns false
//
// # ExistsOne
//
// Comprehension which tests whether exactly one element in a list or map exists which
// satisfies a given predicate expression. This comprehension does not short-circuit in
// keeping with the one-variable exists one macro semantics.
//
// <list>.existsOne(indexVar, valueVar, <predicate>)
// <map>.existsOne(keyVar, valueVar, <predicate>)
//
// This macro may also be used with the `exists_one` function name, for compatibility
// with the one-variable macro of the same name.
//
// Examples:
//
// [1, 2, 1, 3, 1, 4].existsOne(i, v, i == 1 || v == 1) // returns false
// [1, 1, 2, 2, 3, 3].existsOne(i, v, i == 2 && v == 2) // returns true
// {'i': 0, 'j': 1, 'k': 2}.existsOne(i, v, i == 'l' || v == 1) // returns true
//
// # TransformList
//
// Comprehension which converts a map or a list into a list value. The output expression
// of the comprehension determines the contents of the output list. Elements in the list
// may optionally be filtered according to a predicate expression, where elements that
// satisfy the predicate are transformed.
//
// <list>.transformList(indexVar, valueVar, <transform>)
// <list>.transformList(indexVar, valueVar, <filter>, <transform>)
// <map>.transformList(keyVar, valueVar, <transform>)
// <map>.transformList(keyVar, valueVar, <filter>, <transform>)
//
// Examples:
//
// [1, 2, 3].transformList(indexVar, valueVar,
// (indexVar * valueVar) + valueVar) // returns [1, 4, 9]
// [1, 2, 3].transformList(indexVar, valueVar, indexVar % 2 == 0
// (indexVar * valueVar) + valueVar) // returns [1, 9]
// {'greeting': 'hello', 'farewell': 'goodbye'}
// .transformList(k, _, k) // returns ['greeting', 'farewell']
// {'greeting': 'hello', 'farewell': 'goodbye'}
// .transformList(_, v, v) // returns ['hello', 'goodbye']
//
// # TransformMap
//
// Comprehension which converts a map or a list into a map value. The output expression
// of the comprehension determines the value of the output map entry; however, the key
// remains fixed. Elements in the map may optionally be filtered according to a predicate
// expression, where elements that satisfy the predicate are transformed.
//
// <list>.transformMap(indexVar, valueVar, <transform>)
// <list>.transformMap(indexVar, valueVar, <filter>, <transform>)
// <map>.transformMap(keyVar, valueVar, <transform>)
// <map>.transformMap(keyVar, valueVar, <filter>, <transform>)
//
// Examples:
//
// [1, 2, 3].transformMap(indexVar, valueVar,
// (indexVar * valueVar) + valueVar) // returns {0: 1, 1: 4, 2: 9}
// [1, 2, 3].transformMap(indexVar, valueVar, indexVar % 2 == 0
// (indexVar * valueVar) + valueVar) // returns {0: 1, 2: 9}
// {'greeting': 'hello'}.transformMap(k, v, v + '!') // returns {'greeting': 'hello!'}
//
// # TransformMapEntry
//
// Comprehension which converts a map or a list into a map value; however, this transform
// expects the entry expression be a map literal. If the tranform produces an entry which
// duplicates a key in the target map, the comprehension will error. Note, that key
// equality is determined using CEL equality which asserts that numeric values which are
// equal, even if they don't have the same type will cause a key collision.
//
// Elements in the map may optionally be filtered according to a predicate expression, where
// elements that satisfy the predicate are transformed.
//
// <list>.transformMap(indexVar, valueVar, <transform>)
// <list>.transformMap(indexVar, valueVar, <filter>, <transform>)
// <map>.transformMap(keyVar, valueVar, <transform>)
// <map>.transformMap(keyVar, valueVar, <filter>, <transform>)
//
// Examples:
//
// // returns {'hello': 'greeting'}
// {'greeting': 'hello'}.transformMapEntry(keyVar, valueVar, {valueVar: keyVar})
// // reverse lookup, require all values in list be unique
// [1, 2, 3].transformMapEntry(indexVar, valueVar, {valueVar: indexVar})
//
// {'greeting': 'aloha', 'farewell': 'aloha'}
// .transformMapEntry(keyVar, valueVar, {valueVar: keyVar}) // error, duplicate key
func TwoVarComprehensions(options ...TwoVarComprehensionsOption) cel.EnvOption {
l := &compreV2Lib{version: math.MaxUint32}
for _, o := range options {
l = o(l)
}
return cel.Lib(l)
}
// TwoVarComprehensionsOption declares a functional operator for configuring two-variable comprehensions.
type TwoVarComprehensionsOption func(*compreV2Lib) *compreV2Lib
// TwoVarComprehensionsVersion sets the library version for two-variable comprehensions.
func TwoVarComprehensionsVersion(version uint32) TwoVarComprehensionsOption {
return func(lib *compreV2Lib) *compreV2Lib {
lib.version = version
return lib
}
}
type compreV2Lib struct {
version uint32
}
// LibraryName implements that SingletonLibrary interface method.
func (*compreV2Lib) LibraryName() string {
return "cel.lib.ext.comprev2"
}
// CompileOptions implements the cel.Library interface method.
func (*compreV2Lib) CompileOptions() []cel.EnvOption {
kType := cel.TypeParamType("K")
vType := cel.TypeParamType("V")
mapKVType := cel.MapType(kType, vType)
opts := []cel.EnvOption{
cel.Macros(
cel.ReceiverMacro("all", 3, quantifierAll),
cel.ReceiverMacro("exists", 3, quantifierExists),
cel.ReceiverMacro("existsOne", 3, quantifierExistsOne),
cel.ReceiverMacro("exists_one", 3, quantifierExistsOne),
cel.ReceiverMacro("transformList", 3, transformList),
cel.ReceiverMacro("transformList", 4, transformList),
cel.ReceiverMacro("transformMap", 3, transformMap),
cel.ReceiverMacro("transformMap", 4, transformMap),
cel.ReceiverMacro("transformMapEntry", 3, transformMapEntry),
cel.ReceiverMacro("transformMapEntry", 4, transformMapEntry),
),
cel.Function(mapInsert,
cel.Overload(mapInsertOverloadKeyValue, []*cel.Type{mapKVType, kType, vType}, mapKVType,
cel.FunctionBinding(func(args ...ref.Val) ref.Val {
m := args[0].(traits.Mapper)
k := args[1]
v := args[2]
return types.InsertMapKeyValue(m, k, v)
})),
cel.Overload(mapInsertOverloadMap, []*cel.Type{mapKVType, mapKVType}, mapKVType,
cel.BinaryBinding(func(targetMap, updateMap ref.Val) ref.Val {
tm := targetMap.(traits.Mapper)
um := updateMap.(traits.Mapper)
umIt := um.Iterator()
for umIt.HasNext() == types.True {
k := umIt.Next()
updateOrErr := types.InsertMapKeyValue(tm, k, um.Get(k))
if types.IsError(updateOrErr) {
return updateOrErr
}
tm = updateOrErr.(traits.Mapper)
}
return tm
})),
),
}
return opts
}
// ProgramOptions implements the cel.Library interface method
func (*compreV2Lib) ProgramOptions() []cel.ProgramOption {
return []cel.ProgramOption{}
}
func quantifierAll(mef cel.MacroExprFactory, target ast.Expr, args []ast.Expr) (ast.Expr, *cel.Error) {
iterVar1, iterVar2, err := extractIterVars(mef, args[0], args[1])
if err != nil {
return nil, err
}
return mef.NewComprehensionTwoVar(
target,
iterVar1,
iterVar2,
mef.AccuIdentName(),
/*accuInit=*/ mef.NewLiteral(types.True),
/*condition=*/ mef.NewCall(operators.NotStrictlyFalse, mef.NewAccuIdent()),
/*step=*/ mef.NewCall(operators.LogicalAnd, mef.NewAccuIdent(), args[2]),
/*result=*/ mef.NewAccuIdent(),
), nil
}
func quantifierExists(mef cel.MacroExprFactory, target ast.Expr, args []ast.Expr) (ast.Expr, *cel.Error) {
iterVar1, iterVar2, err := extractIterVars(mef, args[0], args[1])
if err != nil {
return nil, err
}
return mef.NewComprehensionTwoVar(
target,
iterVar1,
iterVar2,
mef.AccuIdentName(),
/*accuInit=*/ mef.NewLiteral(types.False),
/*condition=*/ mef.NewCall(operators.NotStrictlyFalse, mef.NewCall(operators.LogicalNot, mef.NewAccuIdent())),
/*step=*/ mef.NewCall(operators.LogicalOr, mef.NewAccuIdent(), args[2]),
/*result=*/ mef.NewAccuIdent(),
), nil
}
func quantifierExistsOne(mef cel.MacroExprFactory, target ast.Expr, args []ast.Expr) (ast.Expr, *cel.Error) {
iterVar1, iterVar2, err := extractIterVars(mef, args[0], args[1])
if err != nil {
return nil, err
}
return mef.NewComprehensionTwoVar(
target,
iterVar1,
iterVar2,
mef.AccuIdentName(),
/*accuInit=*/ mef.NewLiteral(types.Int(0)),
/*condition=*/ mef.NewLiteral(types.True),
/*step=*/ mef.NewCall(operators.Conditional, args[2],
mef.NewCall(operators.Add, mef.NewAccuIdent(), mef.NewLiteral(types.Int(1))),
mef.NewAccuIdent()),
/*result=*/ mef.NewCall(operators.Equals, mef.NewAccuIdent(), mef.NewLiteral(types.Int(1))),
), nil
}
func transformList(mef cel.MacroExprFactory, target ast.Expr, args []ast.Expr) (ast.Expr, *cel.Error) {
iterVar1, iterVar2, err := extractIterVars(mef, args[0], args[1])
if err != nil {
return nil, err
}
var transform ast.Expr
var filter ast.Expr
if len(args) == 4 {
filter = args[2]
transform = args[3]
} else {
filter = nil
transform = args[2]
}
// accumulator = accumulator + [transform]
step := mef.NewCall(operators.Add, mef.NewAccuIdent(), mef.NewList(transform))
if filter != nil {
// accumulator = (filter) ? accumulator + [transform] : accumulator
step = mef.NewCall(operators.Conditional, filter, step, mef.NewAccuIdent())
}
return mef.NewComprehensionTwoVar(
target,
iterVar1,
iterVar2,
mef.AccuIdentName(),
/*accuInit=*/ mef.NewList(),
/*condition=*/ mef.NewLiteral(types.True),
step,
/*result=*/ mef.NewAccuIdent(),
), nil
}
func transformMap(mef cel.MacroExprFactory, target ast.Expr, args []ast.Expr) (ast.Expr, *cel.Error) {
iterVar1, iterVar2, err := extractIterVars(mef, args[0], args[1])
if err != nil {
return nil, err
}
var transform ast.Expr
var filter ast.Expr
if len(args) == 4 {
filter = args[2]
transform = args[3]
} else {
filter = nil
transform = args[2]
}
// accumulator = cel.@mapInsert(accumulator, iterVar1, transform)
step := mef.NewCall(mapInsert, mef.NewAccuIdent(), mef.NewIdent(iterVar1), transform)
if filter != nil {
// accumulator = (filter) ? cel.@mapInsert(accumulator, iterVar1, transform) : accumulator
step = mef.NewCall(operators.Conditional, filter, step, mef.NewAccuIdent())
}
return mef.NewComprehensionTwoVar(
target,
iterVar1,
iterVar2,
mef.AccuIdentName(),
/*accuInit=*/ mef.NewMap(),
/*condition=*/ mef.NewLiteral(types.True),
step,
/*result=*/ mef.NewAccuIdent(),
), nil
}
func transformMapEntry(mef cel.MacroExprFactory, target ast.Expr, args []ast.Expr) (ast.Expr, *cel.Error) {
iterVar1, iterVar2, err := extractIterVars(mef, args[0], args[1])
if err != nil {
return nil, err
}
var transform ast.Expr
var filter ast.Expr
if len(args) == 4 {
filter = args[2]
transform = args[3]
} else {
filter = nil
transform = args[2]
}
// accumulator = cel.@mapInsert(accumulator, transform)
step := mef.NewCall(mapInsert, mef.NewAccuIdent(), transform)
if filter != nil {
// accumulator = (filter) ? cel.@mapInsert(accumulator, transform) : accumulator
step = mef.NewCall(operators.Conditional, filter, step, mef.NewAccuIdent())
}
return mef.NewComprehensionTwoVar(
target,
iterVar1,
iterVar2,
mef.AccuIdentName(),
/*accuInit=*/ mef.NewMap(),
/*condition=*/ mef.NewLiteral(types.True),
step,
/*result=*/ mef.NewAccuIdent(),
), nil
}
func extractIterVars(mef cel.MacroExprFactory, arg0, arg1 ast.Expr) (string, string, *cel.Error) {
iterVar1, err := extractIterVar(mef, arg0)
if err != nil {
return "", "", err
}
iterVar2, err := extractIterVar(mef, arg1)
if err != nil {
return "", "", err
}
if iterVar1 == iterVar2 {
return "", "", mef.NewError(arg1.ID(), fmt.Sprintf("duplicate variable name: %s", iterVar1))
}
if iterVar1 == mef.AccuIdentName() || iterVar1 == parser.AccumulatorName {
return "", "", mef.NewError(arg0.ID(), "iteration variable overwrites accumulator variable")
}
if iterVar2 == mef.AccuIdentName() || iterVar2 == parser.AccumulatorName {
return "", "", mef.NewError(arg1.ID(), "iteration variable overwrites accumulator variable")
}
return iterVar1, iterVar2, nil
}
func extractIterVar(mef cel.MacroExprFactory, target ast.Expr) (string, *cel.Error) {
iterVar, found := extractIdent(target)
if !found {
return "", mef.NewError(target.ID(), "argument must be a simple name")
}
return iterVar, nil
}
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package ext
import (
"encoding/base64"
"math"
"github.com/google/cel-go/cel"
"github.com/google/cel-go/common/types"
"github.com/google/cel-go/common/types/ref"
)
// Encoders returns a cel.EnvOption to configure extended functions for string, byte, and object
// encodings.
//
// # Base64.Decode
//
// Decodes base64-encoded string to bytes.
//
// This function will return an error if the string input is not base64-encoded.
//
// base64.decode(<string>) -> <bytes>
//
// Examples:
//
// base64.decode('aGVsbG8=') // return b'hello'
// base64.decode('aGVsbG8') // return b'hello'
//
// # Base64.Encode
//
// Encodes bytes to a base64-encoded string.
//
// base64.encode(<bytes>) -> <string>
//
// Examples:
//
// base64.encode(b'hello') // return b'aGVsbG8='
func Encoders(options ...EncodersOption) cel.EnvOption {
l := &encoderLib{version: math.MaxUint32}
for _, o := range options {
l = o(l)
}
return cel.Lib(l)
}
// EncodersOption declares a functional operator for configuring encoder extensions.
type EncodersOption func(*encoderLib) *encoderLib
// EncodersVersion sets the library version for encoder extensions.
func EncodersVersion(version uint32) EncodersOption {
return func(lib *encoderLib) *encoderLib {
lib.version = version
return lib
}
}
type encoderLib struct {
version uint32
}
func (*encoderLib) LibraryName() string {
return "cel.lib.ext.encoders"
}
func (*encoderLib) CompileOptions() []cel.EnvOption {
return []cel.EnvOption{
cel.Function("base64.decode",
cel.Overload("base64_decode_string", []*cel.Type{cel.StringType}, cel.BytesType,
cel.UnaryBinding(func(str ref.Val) ref.Val {
s := str.(types.String)
return bytesOrError(base64DecodeString(string(s)))
}))),
cel.Function("base64.encode",
cel.Overload("base64_encode_bytes", []*cel.Type{cel.BytesType}, cel.StringType,
cel.UnaryBinding(func(bytes ref.Val) ref.Val {
b := bytes.(types.Bytes)
return stringOrError(base64EncodeBytes([]byte(b)))
}))),
}
}
func (*encoderLib) ProgramOptions() []cel.ProgramOption {
return []cel.ProgramOption{}
}
func base64DecodeString(str string) ([]byte, error) {
b, err := base64.StdEncoding.DecodeString(str)
if err == nil {
return b, nil
}
if _, tryAltEncoding := err.(base64.CorruptInputError); tryAltEncoding {
return base64.RawStdEncoding.DecodeString(str)
}
return nil, err
}
func base64EncodeBytes(bytes []byte) (string, error) {
return base64.StdEncoding.EncodeToString(bytes), nil
}
// Copyright 2025 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package ext
import (
"fmt"
"github.com/google/cel-go/cel"
"github.com/google/cel-go/common/env"
)
// ExtensionOptionFactory converts an ExtensionConfig value to a CEL environment option.
func ExtensionOptionFactory(configElement any) (cel.EnvOption, bool) {
ext, isExtension := configElement.(*env.Extension)
if !isExtension {
return nil, false
}
fac, found := extFactories[ext.Name]
if !found {
return nil, false
}
// If the version is 'latest', set the version value to the max uint.
ver, err := ext.VersionNumber()
if err != nil {
return func(*cel.Env) (*cel.Env, error) {
return nil, fmt.Errorf("invalid extension version: %s - %s", ext.Name, ext.Version)
}, true
}
return fac(ver), true
}
// extensionFactory accepts a version and produces a CEL environment associated with the versioned extension.
type extensionFactory func(uint32) cel.EnvOption
var extFactories = map[string]extensionFactory{
"bindings": func(version uint32) cel.EnvOption {
return Bindings(BindingsVersion(version))
},
"encoders": func(version uint32) cel.EnvOption {
return Encoders(EncodersVersion(version))
},
"lists": func(version uint32) cel.EnvOption {
return Lists(ListsVersion(version))
},
"math": func(version uint32) cel.EnvOption {
return Math(MathVersion(version))
},
"protos": func(version uint32) cel.EnvOption {
return Protos(ProtosVersion(version))
},
"sets": func(version uint32) cel.EnvOption {
return Sets(SetsVersion(version))
},
"strings": func(version uint32) cel.EnvOption {
return Strings(StringsVersion(version))
},
"two-var-comprehensions": func(version uint32) cel.EnvOption {
return TwoVarComprehensions(TwoVarComprehensionsVersion(version))
},
"regex": func(version uint32) cel.EnvOption {
return Regex(RegexVersion(version))
},
}
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package ext
import (
"errors"
"fmt"
"math"
"sort"
"strconv"
"strings"
"unicode"
"golang.org/x/text/language"
"golang.org/x/text/message"
"github.com/google/cel-go/cel"
"github.com/google/cel-go/common/ast"
"github.com/google/cel-go/common/overloads"
"github.com/google/cel-go/common/types"
"github.com/google/cel-go/common/types/ref"
"github.com/google/cel-go/common/types/traits"
)
type clauseImpl func(ref.Val, string) (string, error)
func clauseForType(argType ref.Type) (clauseImpl, error) {
switch argType {
case types.IntType, types.UintType:
return formatDecimal, nil
case types.StringType, types.BytesType, types.BoolType, types.NullType, types.TypeType:
return FormatString, nil
case types.TimestampType, types.DurationType:
// special case to ensure timestamps/durations get printed as CEL literals
return func(arg ref.Val, locale string) (string, error) {
argStrVal := arg.ConvertToType(types.StringType)
argStr := argStrVal.Value().(string)
if arg.Type() == types.TimestampType {
return fmt.Sprintf("timestamp(%q)", argStr), nil
}
if arg.Type() == types.DurationType {
return fmt.Sprintf("duration(%q)", argStr), nil
}
return "", fmt.Errorf("cannot convert argument of type %s to timestamp/duration", arg.Type().TypeName())
}, nil
case types.ListType:
return formatList, nil
case types.MapType:
return formatMap, nil
case types.DoubleType:
// avoid formatFixed so we can output a period as the decimal separator in order
// to always be a valid CEL literal
return func(arg ref.Val, locale string) (string, error) {
argDouble, ok := arg.Value().(float64)
if !ok {
return "", fmt.Errorf("couldn't convert %s to float64", arg.Type().TypeName())
}
fmtStr := fmt.Sprintf("%%.%df", defaultPrecision)
return fmt.Sprintf(fmtStr, argDouble), nil
}, nil
case types.TypeType:
return func(arg ref.Val, locale string) (string, error) {
return fmt.Sprintf("type(%s)", arg.Value().(string)), nil
}, nil
default:
return nil, fmt.Errorf("no formatting function for %s", argType.TypeName())
}
}
func formatList(arg ref.Val, locale string) (string, error) {
argList := arg.(traits.Lister)
argIterator := argList.Iterator()
var listStrBuilder strings.Builder
_, err := listStrBuilder.WriteRune('[')
if err != nil {
return "", fmt.Errorf("error writing to list string: %w", err)
}
for argIterator.HasNext() == types.True {
member := argIterator.Next()
memberFormat, err := clauseForType(member.Type())
if err != nil {
return "", err
}
unquotedStr, err := memberFormat(member, locale)
if err != nil {
return "", err
}
str := quoteForCEL(member, unquotedStr)
_, err = listStrBuilder.WriteString(str)
if err != nil {
return "", fmt.Errorf("error writing to list string: %w", err)
}
if argIterator.HasNext() == types.True {
_, err = listStrBuilder.WriteString(", ")
if err != nil {
return "", fmt.Errorf("error writing to list string: %w", err)
}
}
}
_, err = listStrBuilder.WriteRune(']')
if err != nil {
return "", fmt.Errorf("error writing to list string: %w", err)
}
return listStrBuilder.String(), nil
}
func formatMap(arg ref.Val, locale string) (string, error) {
argMap := arg.(traits.Mapper)
argIterator := argMap.Iterator()
type mapPair struct {
key string
value string
}
argPairs := make([]mapPair, argMap.Size().Value().(int64))
i := 0
for argIterator.HasNext() == types.True {
key := argIterator.Next()
var keyFormat clauseImpl
switch key.Type() {
case types.StringType, types.BoolType:
keyFormat = FormatString
case types.IntType, types.UintType:
keyFormat = formatDecimal
default:
return "", fmt.Errorf("no formatting function for map key of type %s", key.Type().TypeName())
}
unquotedKeyStr, err := keyFormat(key, locale)
if err != nil {
return "", err
}
keyStr := quoteForCEL(key, unquotedKeyStr)
value, found := argMap.Find(key)
if !found {
return "", fmt.Errorf("could not find key: %q", key)
}
valueFormat, err := clauseForType(value.Type())
if err != nil {
return "", err
}
unquotedValueStr, err := valueFormat(value, locale)
if err != nil {
return "", err
}
valueStr := quoteForCEL(value, unquotedValueStr)
argPairs[i] = mapPair{keyStr, valueStr}
i++
}
sort.SliceStable(argPairs, func(x, y int) bool {
return argPairs[x].key < argPairs[y].key
})
var mapStrBuilder strings.Builder
_, err := mapStrBuilder.WriteRune('{')
if err != nil {
return "", fmt.Errorf("error writing to map string: %w", err)
}
for i, entry := range argPairs {
_, err = mapStrBuilder.WriteString(fmt.Sprintf("%s:%s", entry.key, entry.value))
if err != nil {
return "", fmt.Errorf("error writing to map string: %w", err)
}
if i < len(argPairs)-1 {
_, err = mapStrBuilder.WriteString(", ")
if err != nil {
return "", fmt.Errorf("error writing to map string: %w", err)
}
}
}
_, err = mapStrBuilder.WriteRune('}')
if err != nil {
return "", fmt.Errorf("error writing to map string: %w", err)
}
return mapStrBuilder.String(), nil
}
// quoteForCEL takes a formatted, unquoted value and quotes it in a manner suitable
// for embedding directly in CEL.
func quoteForCEL(refVal ref.Val, unquotedValue string) string {
switch refVal.Type() {
case types.StringType:
return fmt.Sprintf("%q", unquotedValue)
case types.BytesType:
return fmt.Sprintf("b%q", unquotedValue)
case types.DoubleType:
// special case to handle infinity/NaN
num := refVal.Value().(float64)
if math.IsInf(num, 1) || math.IsInf(num, -1) || math.IsNaN(num) {
return fmt.Sprintf("%q", unquotedValue)
}
return unquotedValue
default:
return unquotedValue
}
}
// FormatString returns the string representation of a CEL value.
//
// It is used to implement the %s specifier in the (string).format() extension function.
func FormatString(arg ref.Val, locale string) (string, error) {
switch arg.Type() {
case types.ListType:
return formatList(arg, locale)
case types.MapType:
return formatMap(arg, locale)
case types.IntType, types.UintType, types.DoubleType,
types.BoolType, types.StringType, types.TimestampType, types.BytesType, types.DurationType, types.TypeType:
argStrVal := arg.ConvertToType(types.StringType)
argStr, ok := argStrVal.Value().(string)
if !ok {
return "", fmt.Errorf("could not convert argument %q to string", argStrVal)
}
return argStr, nil
case types.NullType:
return "null", nil
default:
return "", stringFormatError(runtimeID, arg.Type().TypeName())
}
}
func formatDecimal(arg ref.Val, locale string) (string, error) {
switch arg.Type() {
case types.IntType:
argInt, ok := arg.ConvertToType(types.IntType).Value().(int64)
if !ok {
return "", fmt.Errorf("could not convert \"%s\" to int64", arg.Value())
}
return fmt.Sprintf("%d", argInt), nil
case types.UintType:
argInt, ok := arg.ConvertToType(types.UintType).Value().(uint64)
if !ok {
return "", fmt.Errorf("could not convert \"%s\" to uint64", arg.Value())
}
return fmt.Sprintf("%d", argInt), nil
default:
return "", decimalFormatError(runtimeID, arg.Type().TypeName())
}
}
func matchLanguage(locale string) (language.Tag, error) {
matcher, err := makeMatcher(locale)
if err != nil {
return language.Und, err
}
tag, _ := language.MatchStrings(matcher, locale)
return tag, nil
}
func makeMatcher(locale string) (language.Matcher, error) {
tags := make([]language.Tag, 0)
tag, err := language.Parse(locale)
if err != nil {
return nil, err
}
tags = append(tags, tag)
return language.NewMatcher(tags), nil
}
type stringFormatter struct{}
// String implements formatStringInterpolator.String.
func (c *stringFormatter) String(arg ref.Val, locale string) (string, error) {
return FormatString(arg, locale)
}
// Decimal implements formatStringInterpolator.Decimal.
func (c *stringFormatter) Decimal(arg ref.Val, locale string) (string, error) {
return formatDecimal(arg, locale)
}
// Fixed implements formatStringInterpolator.Fixed.
func (c *stringFormatter) Fixed(precision *int) func(ref.Val, string) (string, error) {
if precision == nil {
precision = new(int)
*precision = defaultPrecision
}
return func(arg ref.Val, locale string) (string, error) {
strException := false
if arg.Type() == types.StringType {
argStr := arg.Value().(string)
if argStr == "NaN" || argStr == "Infinity" || argStr == "-Infinity" {
strException = true
}
}
if arg.Type() != types.DoubleType && !strException {
return "", fixedPointFormatError(runtimeID, arg.Type().TypeName())
}
argFloatVal := arg.ConvertToType(types.DoubleType)
argFloat, ok := argFloatVal.Value().(float64)
if !ok {
return "", fmt.Errorf("could not convert \"%s\" to float64", argFloatVal.Value())
}
fmtStr := fmt.Sprintf("%%.%df", *precision)
matchedLocale, err := matchLanguage(locale)
if err != nil {
return "", fmt.Errorf("error matching locale: %w", err)
}
return message.NewPrinter(matchedLocale).Sprintf(fmtStr, argFloat), nil
}
}
// Scientific implements formatStringInterpolator.Scientific.
func (c *stringFormatter) Scientific(precision *int) func(ref.Val, string) (string, error) {
if precision == nil {
precision = new(int)
*precision = defaultPrecision
}
return func(arg ref.Val, locale string) (string, error) {
strException := false
if arg.Type() == types.StringType {
argStr := arg.Value().(string)
if argStr == "NaN" || argStr == "Infinity" || argStr == "-Infinity" {
strException = true
}
}
if arg.Type() != types.DoubleType && !strException {
return "", scientificFormatError(runtimeID, arg.Type().TypeName())
}
argFloatVal := arg.ConvertToType(types.DoubleType)
argFloat, ok := argFloatVal.Value().(float64)
if !ok {
return "", fmt.Errorf("could not convert \"%v\" to float64", argFloatVal.Value())
}
matchedLocale, err := matchLanguage(locale)
if err != nil {
return "", fmt.Errorf("error matching locale: %w", err)
}
fmtStr := fmt.Sprintf("%%%de", *precision)
return message.NewPrinter(matchedLocale).Sprintf(fmtStr, argFloat), nil
}
}
// Binary implements formatStringInterpolator.Binary.
func (c *stringFormatter) Binary(arg ref.Val, locale string) (string, error) {
switch arg.Type() {
case types.IntType:
argInt := arg.Value().(int64)
// locale is intentionally unused as integers formatted as binary
// strings are locale-independent
return fmt.Sprintf("%b", argInt), nil
case types.UintType:
argInt := arg.Value().(uint64)
return fmt.Sprintf("%b", argInt), nil
case types.BoolType:
argBool := arg.Value().(bool)
if argBool {
return "1", nil
}
return "0", nil
default:
return "", binaryFormatError(runtimeID, arg.Type().TypeName())
}
}
// Hex implements formatStringInterpolator.Hex.
func (c *stringFormatter) Hex(useUpper bool) func(ref.Val, string) (string, error) {
return func(arg ref.Val, locale string) (string, error) {
fmtStr := "%x"
if useUpper {
fmtStr = "%X"
}
switch arg.Type() {
case types.StringType, types.BytesType:
if arg.Type() == types.BytesType {
return fmt.Sprintf(fmtStr, arg.Value().([]byte)), nil
}
return fmt.Sprintf(fmtStr, arg.Value().(string)), nil
case types.IntType:
argInt, ok := arg.Value().(int64)
if !ok {
return "", fmt.Errorf("could not convert \"%s\" to int64", arg.Value())
}
return fmt.Sprintf(fmtStr, argInt), nil
case types.UintType:
argInt, ok := arg.Value().(uint64)
if !ok {
return "", fmt.Errorf("could not convert \"%s\" to uint64", arg.Value())
}
return fmt.Sprintf(fmtStr, argInt), nil
default:
return "", hexFormatError(runtimeID, arg.Type().TypeName())
}
}
}
// Octal implements formatStringInterpolator.Octal.
func (c *stringFormatter) Octal(arg ref.Val, locale string) (string, error) {
switch arg.Type() {
case types.IntType:
argInt := arg.Value().(int64)
return fmt.Sprintf("%o", argInt), nil
case types.UintType:
argInt := arg.Value().(uint64)
return fmt.Sprintf("%o", argInt), nil
default:
return "", octalFormatError(runtimeID, arg.Type().TypeName())
}
}
// stringFormatValidator implements the cel.ASTValidator interface allowing for static validation
// of string.format calls.
type stringFormatValidator struct{}
// Name returns the name of the validator.
func (stringFormatValidator) Name() string {
return "cel.validator.string_format"
}
// Configure implements the ASTValidatorConfigurer interface and augments the list of functions to skip
// during homogeneous aggregate literal type-checks.
func (stringFormatValidator) Configure(config cel.MutableValidatorConfig) error {
functions := config.GetOrDefault(cel.HomogeneousAggregateLiteralExemptFunctions, []string{}).([]string)
functions = append(functions, "format")
return config.Set(cel.HomogeneousAggregateLiteralExemptFunctions, functions)
}
// Validate parses all literal format strings and type checks the format clause against the argument
// at the corresponding ordinal within the list literal argument to the function, if one is specified.
func (stringFormatValidator) Validate(env *cel.Env, _ cel.ValidatorConfig, a *ast.AST, iss *cel.Issues) {
root := ast.NavigateAST(a)
formatCallExprs := ast.MatchDescendants(root, matchConstantFormatStringWithListLiteralArgs(a))
for _, e := range formatCallExprs {
call := e.AsCall()
formatStr := call.Target().AsLiteral().Value().(string)
args := call.Args()[0].AsList().Elements()
formatCheck := &stringFormatChecker{
args: args,
ast: a,
}
// use a placeholder locale, since locale doesn't affect syntax
_, err := parseFormatString(formatStr, formatCheck, formatCheck, "en_US")
if err != nil {
iss.ReportErrorAtID(getErrorExprID(e.ID(), err), "%v", err)
continue
}
seenArgs := formatCheck.argsRequested
if len(args) > seenArgs {
iss.ReportErrorAtID(e.ID(),
"too many arguments supplied to string.format (expected %d, got %d)", seenArgs, len(args))
}
}
}
// getErrorExprID determines which list literal argument triggered a type-disagreement for the
// purposes of more accurate error message reports.
func getErrorExprID(id int64, err error) int64 {
fmtErr, ok := err.(formatError)
if ok {
return fmtErr.id
}
wrapped := errors.Unwrap(err)
if wrapped != nil {
return getErrorExprID(id, wrapped)
}
return id
}
// matchConstantFormatStringWithListLiteralArgs matches all valid expression nodes for string
// format checking.
func matchConstantFormatStringWithListLiteralArgs(a *ast.AST) ast.ExprMatcher {
return func(e ast.NavigableExpr) bool {
if e.Kind() != ast.CallKind {
return false
}
call := e.AsCall()
if !call.IsMemberFunction() || call.FunctionName() != "format" {
return false
}
overloadIDs := a.GetOverloadIDs(e.ID())
if len(overloadIDs) != 0 {
found := false
for _, overload := range overloadIDs {
if overload == overloads.ExtFormatString {
found = true
break
}
}
if !found {
return false
}
}
formatString := call.Target()
if formatString.Kind() != ast.LiteralKind || formatString.AsLiteral().Type() != cel.StringType {
return false
}
args := call.Args()
if len(args) != 1 {
return false
}
formatArgs := args[0]
return formatArgs.Kind() == ast.ListKind
}
}
// stringFormatChecker implements the formatStringInterpolater interface
type stringFormatChecker struct {
args []ast.Expr
argsRequested int
currArgIndex int64
ast *ast.AST
}
// String implements formatStringInterpolator.String.
func (c *stringFormatChecker) String(arg ref.Val, locale string) (string, error) {
formatArg := c.args[c.currArgIndex]
valid, badID := c.verifyString(formatArg)
if !valid {
return "", stringFormatError(badID, c.typeOf(badID).TypeName())
}
return "", nil
}
// Decimal implements formatStringInterpolator.Decimal.
func (c *stringFormatChecker) Decimal(arg ref.Val, locale string) (string, error) {
id := c.args[c.currArgIndex].ID()
valid := c.verifyTypeOneOf(id, types.IntType, types.UintType)
if !valid {
return "", decimalFormatError(id, c.typeOf(id).TypeName())
}
return "", nil
}
// Fixed implements formatStringInterpolator.Fixed.
func (c *stringFormatChecker) Fixed(precision *int) func(ref.Val, string) (string, error) {
return func(arg ref.Val, locale string) (string, error) {
id := c.args[c.currArgIndex].ID()
// we allow StringType since "NaN", "Infinity", and "-Infinity" are also valid values
valid := c.verifyTypeOneOf(id, types.DoubleType, types.StringType)
if !valid {
return "", fixedPointFormatError(id, c.typeOf(id).TypeName())
}
return "", nil
}
}
// Scientific implements formatStringInterpolator.Scientific.
func (c *stringFormatChecker) Scientific(precision *int) func(ref.Val, string) (string, error) {
return func(arg ref.Val, locale string) (string, error) {
id := c.args[c.currArgIndex].ID()
valid := c.verifyTypeOneOf(id, types.DoubleType, types.StringType)
if !valid {
return "", scientificFormatError(id, c.typeOf(id).TypeName())
}
return "", nil
}
}
// Binary implements formatStringInterpolator.Binary.
func (c *stringFormatChecker) Binary(arg ref.Val, locale string) (string, error) {
id := c.args[c.currArgIndex].ID()
valid := c.verifyTypeOneOf(id, types.IntType, types.UintType, types.BoolType)
if !valid {
return "", binaryFormatError(id, c.typeOf(id).TypeName())
}
return "", nil
}
// Hex implements formatStringInterpolator.Hex.
func (c *stringFormatChecker) Hex(useUpper bool) func(ref.Val, string) (string, error) {
return func(arg ref.Val, locale string) (string, error) {
id := c.args[c.currArgIndex].ID()
valid := c.verifyTypeOneOf(id, types.IntType, types.UintType, types.StringType, types.BytesType)
if !valid {
return "", hexFormatError(id, c.typeOf(id).TypeName())
}
return "", nil
}
}
// Octal implements formatStringInterpolator.Octal.
func (c *stringFormatChecker) Octal(arg ref.Val, locale string) (string, error) {
id := c.args[c.currArgIndex].ID()
valid := c.verifyTypeOneOf(id, types.IntType, types.UintType)
if !valid {
return "", octalFormatError(id, c.typeOf(id).TypeName())
}
return "", nil
}
// Arg implements formatListArgs.Arg.
func (c *stringFormatChecker) Arg(index int64) (ref.Val, error) {
c.argsRequested++
c.currArgIndex = index
// return a dummy value - this is immediately passed to back to us
// through one of the FormatCallback functions, so anything will do
return types.Int(0), nil
}
// Size implements formatListArgs.Size.
func (c *stringFormatChecker) Size() int64 {
return int64(len(c.args))
}
func (c *stringFormatChecker) typeOf(id int64) *cel.Type {
return c.ast.GetType(id)
}
func (c *stringFormatChecker) verifyTypeOneOf(id int64, validTypes ...*cel.Type) bool {
t := c.typeOf(id)
if t == cel.DynType {
return true
}
for _, vt := range validTypes {
// Only check runtime type compatibility without delving deeper into parameterized types
if t.Kind() == vt.Kind() {
return true
}
}
return false
}
func (c *stringFormatChecker) verifyString(sub ast.Expr) (bool, int64) {
paramA := cel.TypeParamType("A")
paramB := cel.TypeParamType("B")
subVerified := c.verifyTypeOneOf(sub.ID(),
cel.ListType(paramA), cel.MapType(paramA, paramB),
cel.IntType, cel.UintType, cel.DoubleType, cel.BoolType, cel.StringType,
cel.TimestampType, cel.BytesType, cel.DurationType, cel.TypeType, cel.NullType)
if !subVerified {
return false, sub.ID()
}
switch sub.Kind() {
case ast.ListKind:
for _, e := range sub.AsList().Elements() {
// recursively verify if we're dealing with a list/map
verified, id := c.verifyString(e)
if !verified {
return false, id
}
}
return true, sub.ID()
case ast.MapKind:
for _, e := range sub.AsMap().Entries() {
// recursively verify if we're dealing with a list/map
entry := e.AsMapEntry()
verified, id := c.verifyString(entry.Key())
if !verified {
return false, id
}
verified, id = c.verifyString(entry.Value())
if !verified {
return false, id
}
}
return true, sub.ID()
default:
return true, sub.ID()
}
}
// helper routines for reporting common errors during string formatting static validation and
// runtime execution.
func binaryFormatError(id int64, badType string) error {
return newFormatError(id, "only integers and bools can be formatted as binary, was given %s", badType)
}
func decimalFormatError(id int64, badType string) error {
return newFormatError(id, "decimal clause can only be used on integers, was given %s", badType)
}
func fixedPointFormatError(id int64, badType string) error {
return newFormatError(id, "fixed-point clause can only be used on doubles, was given %s", badType)
}
func hexFormatError(id int64, badType string) error {
return newFormatError(id, "only integers, byte buffers, and strings can be formatted as hex, was given %s", badType)
}
func octalFormatError(id int64, badType string) error {
return newFormatError(id, "octal clause can only be used on integers, was given %s", badType)
}
func scientificFormatError(id int64, badType string) error {
return newFormatError(id, "scientific clause can only be used on doubles, was given %s", badType)
}
func stringFormatError(id int64, badType string) error {
return newFormatError(id, "string clause can only be used on strings, bools, bytes, ints, doubles, maps, lists, types, durations, and timestamps, was given %s", badType)
}
type formatError struct {
id int64
msg string
}
func newFormatError(id int64, msg string, args ...any) error {
return formatError{
id: id,
msg: fmt.Sprintf(msg, args...),
}
}
// Error implements error.
func (e formatError) Error() string {
return e.msg
}
// Is implements errors.Is.
func (e formatError) Is(target error) bool {
return e.msg == target.Error()
}
// stringArgList implements the formatListArgs interface.
type stringArgList struct {
args traits.Lister
}
// Arg implements formatListArgs.Arg.
func (c *stringArgList) Arg(index int64) (ref.Val, error) {
if index >= c.args.Size().Value().(int64) {
return nil, fmt.Errorf("index %d out of range", index)
}
return c.args.Get(types.Int(index)), nil
}
// Size implements formatListArgs.Size.
func (c *stringArgList) Size() int64 {
return c.args.Size().Value().(int64)
}
// formatStringInterpolator is an interface that allows user-defined behavior
// for formatting clause implementations, as well as argument retrieval.
// Each function is expected to support the appropriate types as laid out in
// the string.format documentation, and to return an error if given an inappropriate type.
type formatStringInterpolator interface {
// String takes a ref.Val and a string representing the current locale identifier
// and returns the Val formatted as a string, or an error if one occurred.
String(ref.Val, string) (string, error)
// Decimal takes a ref.Val and a string representing the current locale identifier
// and returns the Val formatted as a decimal integer, or an error if one occurred.
Decimal(ref.Val, string) (string, error)
// Fixed takes an int pointer representing precision (or nil if none was given) and
// returns a function operating in a similar manner to String and Decimal, taking a
// ref.Val and locale and returning the appropriate string. A closure is returned
// so precision can be set without needing an additional function call/configuration.
Fixed(*int) func(ref.Val, string) (string, error)
// Scientific functions identically to Fixed, except the string returned from the closure
// is expected to be in scientific notation.
Scientific(*int) func(ref.Val, string) (string, error)
// Binary takes a ref.Val and a string representing the current locale identifier
// and returns the Val formatted as a binary integer, or an error if one occurred.
Binary(ref.Val, string) (string, error)
// Hex takes a boolean that, if true, indicates the hex string output by the returned
// closure should use uppercase letters for A-F.
Hex(bool) func(ref.Val, string) (string, error)
// Octal takes a ref.Val and a string representing the current locale identifier and
// returns the Val formatted in octal, or an error if one occurred.
Octal(ref.Val, string) (string, error)
}
// formatListArgs is an interface that allows user-defined list-like datatypes to be used
// for formatting clause implementations.
type formatListArgs interface {
// Arg returns the ref.Val at the given index, or an error if one occurred.
Arg(int64) (ref.Val, error)
// Size returns the length of the argument list.
Size() int64
}
// parseFormatString formats a string according to the string.format syntax, taking the clause implementations
// from the provided FormatCallback and the args from the given FormatList.
func parseFormatString(formatStr string, callback formatStringInterpolator, list formatListArgs, locale string) (string, error) {
i := 0
argIndex := 0
var builtStr strings.Builder
for i < len(formatStr) {
if formatStr[i] == '%' {
if i+1 < len(formatStr) && formatStr[i+1] == '%' {
err := builtStr.WriteByte('%')
if err != nil {
return "", fmt.Errorf("error writing format string: %w", err)
}
i += 2
continue
} else {
argAny, err := list.Arg(int64(argIndex))
if err != nil {
return "", err
}
if i+1 >= len(formatStr) {
return "", errors.New("unexpected end of string")
}
if int64(argIndex) >= list.Size() {
return "", fmt.Errorf("index %d out of range", argIndex)
}
numRead, val, refErr := parseAndFormatClause(formatStr[i:], argAny, callback, list, locale)
if refErr != nil {
return "", refErr
}
_, err = builtStr.WriteString(val)
if err != nil {
return "", fmt.Errorf("error writing format string: %w", err)
}
i += numRead
argIndex++
}
} else {
err := builtStr.WriteByte(formatStr[i])
if err != nil {
return "", fmt.Errorf("error writing format string: %w", err)
}
i++
}
}
return builtStr.String(), nil
}
// parseAndFormatClause parses the format clause at the start of the given string with val, and returns
// how many characters were consumed and the substituted string form of val, or an error if one occurred.
func parseAndFormatClause(formatStr string, val ref.Val, callback formatStringInterpolator, list formatListArgs, locale string) (int, string, error) {
i := 1
read, formatter, err := parseFormattingClause(formatStr[i:], callback)
i += read
if err != nil {
return -1, "", newParseFormatError("could not parse formatting clause", err)
}
valStr, err := formatter(val, locale)
if err != nil {
return -1, "", newParseFormatError("error during formatting", err)
}
return i, valStr, nil
}
func parseFormattingClause(formatStr string, callback formatStringInterpolator) (int, clauseImpl, error) {
i := 0
read, precision, err := parsePrecision(formatStr[i:])
i += read
if err != nil {
return -1, nil, fmt.Errorf("error while parsing precision: %w", err)
}
r := rune(formatStr[i])
i++
switch r {
case 's':
return i, callback.String, nil
case 'd':
return i, callback.Decimal, nil
case 'f':
return i, callback.Fixed(precision), nil
case 'e':
return i, callback.Scientific(precision), nil
case 'b':
return i, callback.Binary, nil
case 'x', 'X':
return i, callback.Hex(unicode.IsUpper(r)), nil
case 'o':
return i, callback.Octal, nil
default:
return -1, nil, fmt.Errorf("unrecognized formatting clause \"%c\"", r)
}
}
func parsePrecision(formatStr string) (int, *int, error) {
i := 0
if formatStr[i] != '.' {
return i, nil, nil
}
i++
var buffer strings.Builder
for {
if i >= len(formatStr) {
return -1, nil, errors.New("could not find end of precision specifier")
}
if !isASCIIDigit(rune(formatStr[i])) {
break
}
buffer.WriteByte(formatStr[i])
i++
}
precision, err := strconv.Atoi(buffer.String())
if err != nil {
return -1, nil, fmt.Errorf("error while converting precision to integer: %w", err)
}
return i, &precision, nil
}
func isASCIIDigit(r rune) bool {
return r <= unicode.MaxASCII && unicode.IsDigit(r)
}
type parseFormatError struct {
msg string
wrapped error
}
func newParseFormatError(msg string, wrapped error) error {
return parseFormatError{msg: msg, wrapped: wrapped}
}
// Error implements error.
func (e parseFormatError) Error() string {
return fmt.Sprintf("%s: %s", e.msg, e.wrapped.Error())
}
// Is implements errors.Is.
func (e parseFormatError) Is(target error) bool {
return e.Error() == target.Error()
}
// Is implements errors.Unwrap.
func (e parseFormatError) Unwrap() error {
return e.wrapped
}
const (
runtimeID = int64(-1)
)
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package ext
import (
"errors"
"fmt"
"math"
"sort"
"strconv"
"strings"
"time"
"unicode"
"github.com/google/cel-go/cel"
"github.com/google/cel-go/common/ast"
"github.com/google/cel-go/common/types"
"github.com/google/cel-go/common/types/ref"
"github.com/google/cel-go/common/types/traits"
)
type clauseImplV2 func(ref.Val) (string, error)
type appendingFormatterV2 struct {
buf []byte
}
type formattedMapEntryV2 struct {
key string
val string
}
func (af *appendingFormatterV2) format(arg ref.Val) error {
switch arg.Type() {
case types.BoolType:
argBool, ok := arg.Value().(bool)
if !ok {
return fmt.Errorf("type conversion error from '%s' to '%s'", arg.Type(), types.BoolType)
}
af.buf = strconv.AppendBool(af.buf, argBool)
return nil
case types.IntType:
argInt, ok := arg.Value().(int64)
if !ok {
return fmt.Errorf("type conversion error from '%s' to '%s'", arg.Type(), types.IntType)
}
af.buf = strconv.AppendInt(af.buf, argInt, 10)
return nil
case types.UintType:
argUint, ok := arg.Value().(uint64)
if !ok {
return fmt.Errorf("type conversion error from '%s' to '%s'", arg.Type(), types.UintType)
}
af.buf = strconv.AppendUint(af.buf, argUint, 10)
return nil
case types.DoubleType:
argDbl, ok := arg.Value().(float64)
if !ok {
return fmt.Errorf("type conversion error from '%s' to '%s'", arg.Type(), types.DoubleType)
}
if math.IsNaN(argDbl) {
af.buf = append(af.buf, "NaN"...)
return nil
}
if math.IsInf(argDbl, -1) {
af.buf = append(af.buf, "-Infinity"...)
return nil
}
if math.IsInf(argDbl, 1) {
af.buf = append(af.buf, "Infinity"...)
return nil
}
af.buf = strconv.AppendFloat(af.buf, argDbl, 'f', -1, 64)
return nil
case types.BytesType:
argBytes, ok := arg.Value().([]byte)
if !ok {
return fmt.Errorf("type conversion error from '%s' to '%s'", arg.Type(), types.BytesType)
}
af.buf = append(af.buf, argBytes...)
return nil
case types.StringType:
argStr, ok := arg.Value().(string)
if !ok {
return fmt.Errorf("type conversion error from '%s' to '%s'", arg.Type(), types.StringType)
}
af.buf = append(af.buf, argStr...)
return nil
case types.DurationType:
argDur, ok := arg.Value().(time.Duration)
if !ok {
return fmt.Errorf("type conversion error from '%s' to '%s'", arg.Type(), types.DurationType)
}
af.buf = strconv.AppendFloat(af.buf, argDur.Seconds(), 'f', -1, 64)
af.buf = append(af.buf, "s"...)
return nil
case types.TimestampType:
argTime, ok := arg.Value().(time.Time)
if !ok {
return fmt.Errorf("type conversion error from '%s' to '%s'", arg.Type(), types.TimestampType)
}
af.buf = argTime.UTC().AppendFormat(af.buf, time.RFC3339Nano)
return nil
case types.NullType:
af.buf = append(af.buf, "null"...)
return nil
case types.TypeType:
argType, ok := arg.Value().(string)
if !ok {
return fmt.Errorf("type conversion error from '%s' to '%s'", arg.Type(), types.TypeType)
}
af.buf = append(af.buf, argType...)
return nil
case types.ListType:
argList, ok := arg.(traits.Lister)
if !ok {
return fmt.Errorf("type conversion error from '%s' to '%s'", arg.Type(), types.ListType)
}
argIter := argList.Iterator()
af.buf = append(af.buf, "["...)
if argIter.HasNext() == types.True {
if err := af.format(argIter.Next()); err != nil {
return err
}
for argIter.HasNext() == types.True {
af.buf = append(af.buf, ", "...)
if err := af.format(argIter.Next()); err != nil {
return err
}
}
}
af.buf = append(af.buf, "]"...)
return nil
case types.MapType:
argMap, ok := arg.(traits.Mapper)
if !ok {
return fmt.Errorf("type conversion error from '%s' to '%s'", arg.Type(), types.MapType)
}
argIter := argMap.Iterator()
ents := []formattedMapEntryV2{}
for argIter.HasNext() == types.True {
key := argIter.Next()
val, ok := argMap.Find(key)
if !ok {
return fmt.Errorf("key missing from map: '%s'", key)
}
keyStr, err := formatStringV2(key)
if err != nil {
return err
}
valStr, err := formatStringV2(val)
if err != nil {
return err
}
ents = append(ents, formattedMapEntryV2{keyStr, valStr})
}
sort.SliceStable(ents, func(x, y int) bool {
return ents[x].key < ents[y].key
})
af.buf = append(af.buf, "{"...)
for i, e := range ents {
if i > 0 {
af.buf = append(af.buf, ", "...)
}
af.buf = append(af.buf, e.key...)
af.buf = append(af.buf, ": "...)
af.buf = append(af.buf, e.val...)
}
af.buf = append(af.buf, "}"...)
return nil
default:
return stringFormatErrorV2(runtimeID, arg.Type().TypeName())
}
}
func formatStringV2(arg ref.Val) (string, error) {
var fmter appendingFormatterV2
if err := fmter.format(arg); err != nil {
return "", err
}
return string(fmter.buf), nil
}
type stringFormatterV2 struct{}
// String implements formatStringInterpolatorV2.String.
func (c *stringFormatterV2) String(arg ref.Val) (string, error) {
return formatStringV2(arg)
}
// Decimal implements formatStringInterpolatorV2.Decimal.
func (c *stringFormatterV2) Decimal(arg ref.Val) (string, error) {
switch arg.Type() {
case types.IntType:
argInt, ok := arg.Value().(int64)
if !ok {
return "", fmt.Errorf("type conversion error from '%s' to '%s'", arg.Type(), types.IntType)
}
return strconv.FormatInt(argInt, 10), nil
case types.UintType:
argUint, ok := arg.Value().(uint64)
if !ok {
return "", fmt.Errorf("type conversion error from '%s' to '%s'", arg.Type(), types.UintType)
}
return strconv.FormatUint(argUint, 10), nil
case types.DoubleType:
argDbl, ok := arg.Value().(float64)
if !ok {
return "", fmt.Errorf("type conversion error from '%s' to '%s'", arg.Type(), types.DoubleType)
}
if math.IsNaN(argDbl) {
return "NaN", nil
}
if math.IsInf(argDbl, -1) {
return "-Infinity", nil
}
if math.IsInf(argDbl, 1) {
return "Infinity", nil
}
return strconv.FormatFloat(argDbl, 'f', -1, 64), nil
default:
return "", decimalFormatErrorV2(runtimeID, arg.Type().TypeName())
}
}
// Fixed implements formatStringInterpolatorV2.Fixed.
func (c *stringFormatterV2) Fixed(precision int) func(ref.Val) (string, error) {
return func(arg ref.Val) (string, error) {
fmtStr := fmt.Sprintf("%%.%df", precision)
switch arg.Type() {
case types.IntType:
argInt, ok := arg.Value().(int64)
if !ok {
return "", fmt.Errorf("type conversion error from '%s' to '%s'", arg.Type(), types.IntType)
}
return fmt.Sprintf(fmtStr, argInt), nil
case types.UintType:
argUint, ok := arg.Value().(uint64)
if !ok {
return "", fmt.Errorf("type conversion error from '%s' to '%s'", arg.Type(), types.UintType)
}
return fmt.Sprintf(fmtStr, argUint), nil
case types.DoubleType:
argDbl, ok := arg.Value().(float64)
if !ok {
return "", fmt.Errorf("type conversion error from '%s' to '%s'", arg.Type(), types.DoubleType)
}
if math.IsNaN(argDbl) {
return "NaN", nil
}
if math.IsInf(argDbl, -1) {
return "-Infinity", nil
}
if math.IsInf(argDbl, 1) {
return "Infinity", nil
}
return fmt.Sprintf(fmtStr, argDbl), nil
default:
return "", fixedPointFormatErrorV2(runtimeID, arg.Type().TypeName())
}
}
}
// Scientific implements formatStringInterpolatorV2.Scientific.
func (c *stringFormatterV2) Scientific(precision int) func(ref.Val) (string, error) {
return func(arg ref.Val) (string, error) {
fmtStr := fmt.Sprintf("%%1.%de", precision)
switch arg.Type() {
case types.IntType:
argInt, ok := arg.Value().(int64)
if !ok {
return "", fmt.Errorf("type conversion error from '%s' to '%s'", arg.Type(), types.IntType)
}
return fmt.Sprintf(fmtStr, argInt), nil
case types.UintType:
argUint, ok := arg.Value().(uint64)
if !ok {
return "", fmt.Errorf("type conversion error from '%s' to '%s'", arg.Type(), types.UintType)
}
return fmt.Sprintf(fmtStr, argUint), nil
case types.DoubleType:
argDbl, ok := arg.Value().(float64)
if !ok {
return "", fmt.Errorf("type conversion error from '%s' to '%s'", arg.Type(), types.DoubleType)
}
if math.IsNaN(argDbl) {
return "NaN", nil
}
if math.IsInf(argDbl, -1) {
return "-Infinity", nil
}
if math.IsInf(argDbl, 1) {
return "Infinity", nil
}
return fmt.Sprintf(fmtStr, argDbl), nil
default:
return "", scientificFormatErrorV2(runtimeID, arg.Type().TypeName())
}
}
}
// Binary implements formatStringInterpolatorV2.Binary.
func (c *stringFormatterV2) Binary(arg ref.Val) (string, error) {
switch arg.Type() {
case types.BoolType:
argBool, ok := arg.Value().(bool)
if !ok {
return "", fmt.Errorf("type conversion error from '%s' to '%s'", arg.Type(), types.BoolType)
}
if argBool {
return "1", nil
}
return "0", nil
case types.IntType:
argInt, ok := arg.Value().(int64)
if !ok {
return "", fmt.Errorf("type conversion error from '%s' to '%s'", arg.Type(), types.IntType)
}
return strconv.FormatInt(argInt, 2), nil
case types.UintType:
argUint, ok := arg.Value().(uint64)
if !ok {
return "", fmt.Errorf("type conversion error from '%s' to '%s'", arg.Type(), types.UintType)
}
return strconv.FormatUint(argUint, 2), nil
default:
return "", binaryFormatErrorV2(runtimeID, arg.Type().TypeName())
}
}
// Hex implements formatStringInterpolatorV2.Hex.
func (c *stringFormatterV2) Hex(useUpper bool) func(ref.Val) (string, error) {
return func(arg ref.Val) (string, error) {
var fmtStr string
if useUpper {
fmtStr = "%X"
} else {
fmtStr = "%x"
}
switch arg.Type() {
case types.IntType:
argInt, ok := arg.Value().(int64)
if !ok {
return "", fmt.Errorf("type conversion error from '%s' to '%s'", arg.Type(), types.IntType)
}
return fmt.Sprintf(fmtStr, argInt), nil
case types.UintType:
argUint, ok := arg.Value().(uint64)
if !ok {
return "", fmt.Errorf("type conversion error from '%s' to '%s'", arg.Type(), types.UintType)
}
return fmt.Sprintf(fmtStr, argUint), nil
case types.StringType:
argStr, ok := arg.Value().(string)
if !ok {
return "", fmt.Errorf("type conversion error from '%s' to '%s'", arg.Type(), types.StringType)
}
return fmt.Sprintf(fmtStr, argStr), nil
case types.BytesType:
argBytes, ok := arg.Value().([]byte)
if !ok {
return "", fmt.Errorf("type conversion error from '%s' to '%s'", arg.Type(), types.BytesType)
}
return fmt.Sprintf(fmtStr, argBytes), nil
default:
return "", hexFormatErrorV2(runtimeID, arg.Type().TypeName())
}
}
}
// Octal implements formatStringInterpolatorV2.Octal.
func (c *stringFormatterV2) Octal(arg ref.Val) (string, error) {
switch arg.Type() {
case types.IntType:
argInt, ok := arg.Value().(int64)
if !ok {
return "", fmt.Errorf("type conversion error from '%s' to '%s'", arg.Type(), types.IntType)
}
return strconv.FormatInt(argInt, 8), nil
case types.UintType:
argUint, ok := arg.Value().(uint64)
if !ok {
return "", fmt.Errorf("type conversion error from '%s' to '%s'", arg.Type(), types.UintType)
}
return strconv.FormatUint(argUint, 8), nil
default:
return "", octalFormatErrorV2(runtimeID, arg.Type().TypeName())
}
}
// stringFormatValidatorV2 implements the cel.ASTValidator interface allowing for static validation
// of string.format calls.
type stringFormatValidatorV2 struct{}
// Name returns the name of the validator.
func (stringFormatValidatorV2) Name() string {
return "cel.validator.string_format"
}
// Configure implements the ASTValidatorConfigurer interface and augments the list of functions to skip
// during homogeneous aggregate literal type-checks.
func (stringFormatValidatorV2) Configure(config cel.MutableValidatorConfig) error {
functions := config.GetOrDefault(cel.HomogeneousAggregateLiteralExemptFunctions, []string{}).([]string)
functions = append(functions, "format")
return config.Set(cel.HomogeneousAggregateLiteralExemptFunctions, functions)
}
// Validate parses all literal format strings and type checks the format clause against the argument
// at the corresponding ordinal within the list literal argument to the function, if one is specified.
func (stringFormatValidatorV2) Validate(env *cel.Env, _ cel.ValidatorConfig, a *ast.AST, iss *cel.Issues) {
root := ast.NavigateAST(a)
formatCallExprs := ast.MatchDescendants(root, matchConstantFormatStringWithListLiteralArgs(a))
for _, e := range formatCallExprs {
call := e.AsCall()
formatStr := call.Target().AsLiteral().Value().(string)
args := call.Args()[0].AsList().Elements()
formatCheck := &stringFormatCheckerV2{
args: args,
ast: a,
}
// use a placeholder locale, since locale doesn't affect syntax
_, err := parseFormatStringV2(formatStr, formatCheck, formatCheck)
if err != nil {
iss.ReportErrorAtID(getErrorExprID(e.ID(), err), "%v", err)
continue
}
seenArgs := formatCheck.argsRequested
if len(args) > seenArgs {
iss.ReportErrorAtID(e.ID(),
"too many arguments supplied to string.format (expected %d, got %d)", seenArgs, len(args))
}
}
}
// stringFormatCheckerV2 implements the formatStringInterpolater interface
type stringFormatCheckerV2 struct {
args []ast.Expr
argsRequested int
currArgIndex int64
ast *ast.AST
}
// String implements formatStringInterpolatorV2.String.
func (c *stringFormatCheckerV2) String(arg ref.Val) (string, error) {
formatArg := c.args[c.currArgIndex]
valid, badID := c.verifyString(formatArg)
if !valid {
return "", stringFormatErrorV2(badID, c.typeOf(badID).TypeName())
}
return "", nil
}
// Decimal implements formatStringInterpolatorV2.Decimal.
func (c *stringFormatCheckerV2) Decimal(arg ref.Val) (string, error) {
id := c.args[c.currArgIndex].ID()
valid := c.verifyTypeOneOf(id, types.IntType, types.UintType, types.DoubleType)
if !valid {
return "", decimalFormatErrorV2(id, c.typeOf(id).TypeName())
}
return "", nil
}
// Fixed implements formatStringInterpolatorV2.Fixed.
func (c *stringFormatCheckerV2) Fixed(precision int) func(ref.Val) (string, error) {
return func(arg ref.Val) (string, error) {
id := c.args[c.currArgIndex].ID()
valid := c.verifyTypeOneOf(id, types.IntType, types.UintType, types.DoubleType)
if !valid {
return "", fixedPointFormatErrorV2(id, c.typeOf(id).TypeName())
}
return "", nil
}
}
// Scientific implements formatStringInterpolatorV2.Scientific.
func (c *stringFormatCheckerV2) Scientific(precision int) func(ref.Val) (string, error) {
return func(arg ref.Val) (string, error) {
id := c.args[c.currArgIndex].ID()
valid := c.verifyTypeOneOf(id, types.IntType, types.UintType, types.DoubleType)
if !valid {
return "", scientificFormatErrorV2(id, c.typeOf(id).TypeName())
}
return "", nil
}
}
// Binary implements formatStringInterpolatorV2.Binary.
func (c *stringFormatCheckerV2) Binary(arg ref.Val) (string, error) {
id := c.args[c.currArgIndex].ID()
valid := c.verifyTypeOneOf(id, types.BoolType, types.IntType, types.UintType)
if !valid {
return "", binaryFormatErrorV2(id, c.typeOf(id).TypeName())
}
return "", nil
}
// Hex implements formatStringInterpolatorV2.Hex.
func (c *stringFormatCheckerV2) Hex(useUpper bool) func(ref.Val) (string, error) {
return func(arg ref.Val) (string, error) {
id := c.args[c.currArgIndex].ID()
valid := c.verifyTypeOneOf(id, types.IntType, types.UintType, types.StringType, types.BytesType)
if !valid {
return "", hexFormatErrorV2(id, c.typeOf(id).TypeName())
}
return "", nil
}
}
// Octal implements formatStringInterpolatorV2.Octal.
func (c *stringFormatCheckerV2) Octal(arg ref.Val) (string, error) {
id := c.args[c.currArgIndex].ID()
valid := c.verifyTypeOneOf(id, types.IntType, types.UintType)
if !valid {
return "", octalFormatErrorV2(id, c.typeOf(id).TypeName())
}
return "", nil
}
// Arg implements formatListArgs.Arg.
func (c *stringFormatCheckerV2) Arg(index int64) (ref.Val, error) {
c.argsRequested++
c.currArgIndex = index
// return a dummy value - this is immediately passed to back to us
// through one of the FormatCallback functions, so anything will do
return types.Int(0), nil
}
// Size implements formatListArgs.Size.
func (c *stringFormatCheckerV2) Size() int64 {
return int64(len(c.args))
}
func (c *stringFormatCheckerV2) typeOf(id int64) *cel.Type {
return c.ast.GetType(id)
}
func (c *stringFormatCheckerV2) verifyTypeOneOf(id int64, validTypes ...*cel.Type) bool {
t := c.typeOf(id)
if t == cel.DynType {
return true
}
for _, vt := range validTypes {
// Only check runtime type compatibility without delving deeper into parameterized types
if t.Kind() == vt.Kind() {
return true
}
}
return false
}
func (c *stringFormatCheckerV2) verifyString(sub ast.Expr) (bool, int64) {
paramA := cel.TypeParamType("A")
paramB := cel.TypeParamType("B")
subVerified := c.verifyTypeOneOf(sub.ID(),
cel.ListType(paramA), cel.MapType(paramA, paramB),
cel.IntType, cel.UintType, cel.DoubleType, cel.BoolType, cel.StringType,
cel.TimestampType, cel.BytesType, cel.DurationType, cel.TypeType, cel.NullType)
if !subVerified {
return false, sub.ID()
}
switch sub.Kind() {
case ast.ListKind:
for _, e := range sub.AsList().Elements() {
// recursively verify if we're dealing with a list/map
verified, id := c.verifyString(e)
if !verified {
return false, id
}
}
return true, sub.ID()
case ast.MapKind:
for _, e := range sub.AsMap().Entries() {
// recursively verify if we're dealing with a list/map
entry := e.AsMapEntry()
verified, id := c.verifyString(entry.Key())
if !verified {
return false, id
}
verified, id = c.verifyString(entry.Value())
if !verified {
return false, id
}
}
return true, sub.ID()
default:
return true, sub.ID()
}
}
// helper routines for reporting common errors during string formatting static validation and
// runtime execution.
func binaryFormatErrorV2(id int64, badType string) error {
return newFormatError(id, "only ints, uints, and bools can be formatted as binary, was given %s", badType)
}
func decimalFormatErrorV2(id int64, badType string) error {
return newFormatError(id, "decimal clause can only be used on ints, uints, and doubles, was given %s", badType)
}
func fixedPointFormatErrorV2(id int64, badType string) error {
return newFormatError(id, "fixed-point clause can only be used on ints, uints, and doubles, was given %s", badType)
}
func hexFormatErrorV2(id int64, badType string) error {
return newFormatError(id, "only ints, uints, bytes, and strings can be formatted as hex, was given %s", badType)
}
func octalFormatErrorV2(id int64, badType string) error {
return newFormatError(id, "octal clause can only be used on ints and uints, was given %s", badType)
}
func scientificFormatErrorV2(id int64, badType string) error {
return newFormatError(id, "scientific clause can only be used on ints, uints, and doubles, was given %s", badType)
}
func stringFormatErrorV2(id int64, badType string) error {
return newFormatError(id, "string clause can only be used on strings, bools, bytes, ints, doubles, maps, lists, types, durations, and timestamps, was given %s", badType)
}
// formatStringInterpolatorV2 is an interface that allows user-defined behavior
// for formatting clause implementations, as well as argument retrieval.
// Each function is expected to support the appropriate types as laid out in
// the string.format documentation, and to return an error if given an inappropriate type.
type formatStringInterpolatorV2 interface {
// String takes a ref.Val and a string representing the current locale identifier
// and returns the Val formatted as a string, or an error if one occurred.
String(ref.Val) (string, error)
// Decimal takes a ref.Val and a string representing the current locale identifier
// and returns the Val formatted as a decimal integer, or an error if one occurred.
Decimal(ref.Val) (string, error)
// Fixed takes an int pointer representing precision (or nil if none was given) and
// returns a function operating in a similar manner to String and Decimal, taking a
// ref.Val and locale and returning the appropriate string. A closure is returned
// so precision can be set without needing an additional function call/configuration.
Fixed(int) func(ref.Val) (string, error)
// Scientific functions identically to Fixed, except the string returned from the closure
// is expected to be in scientific notation.
Scientific(int) func(ref.Val) (string, error)
// Binary takes a ref.Val and a string representing the current locale identifier
// and returns the Val formatted as a binary integer, or an error if one occurred.
Binary(ref.Val) (string, error)
// Hex takes a boolean that, if true, indicates the hex string output by the returned
// closure should use uppercase letters for A-F.
Hex(bool) func(ref.Val) (string, error)
// Octal takes a ref.Val and a string representing the current locale identifier and
// returns the Val formatted in octal, or an error if one occurred.
Octal(ref.Val) (string, error)
}
// parseFormatString formats a string according to the string.format syntax, taking the clause implementations
// from the provided FormatCallback and the args from the given FormatList.
func parseFormatStringV2(formatStr string, callback formatStringInterpolatorV2, list formatListArgs) (string, error) {
i := 0
argIndex := 0
var builtStr strings.Builder
for i < len(formatStr) {
if formatStr[i] == '%' {
if i+1 < len(formatStr) && formatStr[i+1] == '%' {
err := builtStr.WriteByte('%')
if err != nil {
return "", fmt.Errorf("error writing format string: %w", err)
}
i += 2
continue
} else {
argAny, err := list.Arg(int64(argIndex))
if err != nil {
return "", err
}
if i+1 >= len(formatStr) {
return "", errors.New("unexpected end of string")
}
if int64(argIndex) >= list.Size() {
return "", fmt.Errorf("index %d out of range", argIndex)
}
numRead, val, refErr := parseAndFormatClauseV2(formatStr[i:], argAny, callback, list)
if refErr != nil {
return "", refErr
}
_, err = builtStr.WriteString(val)
if err != nil {
return "", fmt.Errorf("error writing format string: %w", err)
}
i += numRead
argIndex++
}
} else {
err := builtStr.WriteByte(formatStr[i])
if err != nil {
return "", fmt.Errorf("error writing format string: %w", err)
}
i++
}
}
return builtStr.String(), nil
}
// parseAndFormatClause parses the format clause at the start of the given string with val, and returns
// how many characters were consumed and the substituted string form of val, or an error if one occurred.
func parseAndFormatClauseV2(formatStr string, val ref.Val, callback formatStringInterpolatorV2, list formatListArgs) (int, string, error) {
i := 1
read, formatter, err := parseFormattingClauseV2(formatStr[i:], callback)
i += read
if err != nil {
return -1, "", newParseFormatError("could not parse formatting clause", err)
}
valStr, err := formatter(val)
if err != nil {
return -1, "", newParseFormatError("error during formatting", err)
}
return i, valStr, nil
}
func parseFormattingClauseV2(formatStr string, callback formatStringInterpolatorV2) (int, clauseImplV2, error) {
i := 0
read, precision, err := parsePrecisionV2(formatStr[i:])
i += read
if err != nil {
return -1, nil, fmt.Errorf("error while parsing precision: %w", err)
}
r := rune(formatStr[i])
i++
switch r {
case 's':
return i, callback.String, nil
case 'd':
return i, callback.Decimal, nil
case 'f':
return i, callback.Fixed(precision), nil
case 'e':
return i, callback.Scientific(precision), nil
case 'b':
return i, callback.Binary, nil
case 'x', 'X':
return i, callback.Hex(unicode.IsUpper(r)), nil
case 'o':
return i, callback.Octal, nil
default:
return -1, nil, fmt.Errorf("unrecognized formatting clause \"%c\"", r)
}
}
func parsePrecisionV2(formatStr string) (int, int, error) {
i := 0
if formatStr[i] != '.' {
return i, defaultPrecision, nil
}
i++
var buffer strings.Builder
for {
if i >= len(formatStr) {
return -1, -1, errors.New("could not find end of precision specifier")
}
if !isASCIIDigit(rune(formatStr[i])) {
break
}
buffer.WriteByte(formatStr[i])
i++
}
precision, err := strconv.Atoi(buffer.String())
if err != nil {
return -1, -1, fmt.Errorf("error while converting precision to integer: %w", err)
}
if precision < 0 {
return -1, -1, fmt.Errorf("negative precision: %d", precision)
}
return i, precision, nil
}
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package ext
import (
"github.com/google/cel-go/common/ast"
"github.com/google/cel-go/common/types"
"github.com/google/cel-go/common/types/ref"
)
// function invocation guards for common call signatures within extension functions.
func intOrError(i int64, err error) ref.Val {
if err != nil {
return types.NewErrFromString(err.Error())
}
return types.Int(i)
}
func bytesOrError(bytes []byte, err error) ref.Val {
if err != nil {
return types.NewErrFromString(err.Error())
}
return types.Bytes(bytes)
}
func stringOrError(str string, err error) ref.Val {
if err != nil {
return types.NewErrFromString(err.Error())
}
return types.String(str)
}
func listStringOrError(strs []string, err error) ref.Val {
if err != nil {
return types.NewErrFromString(err.Error())
}
return types.DefaultTypeAdapter.NativeToValue(strs)
}
func extractIdent(target ast.Expr) (string, bool) {
switch target.Kind() {
case ast.IdentKind:
return target.AsIdent(), true
default:
return "", false
}
}
func macroTargetMatchesNamespace(ns string, target ast.Expr) bool {
if id, found := extractIdent(target); found {
return id == ns
}
return false
}
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package ext
import (
"fmt"
"math"
"sort"
"github.com/google/cel-go/cel"
"github.com/google/cel-go/checker"
"github.com/google/cel-go/common"
"github.com/google/cel-go/common/ast"
"github.com/google/cel-go/common/decls"
"github.com/google/cel-go/common/types"
"github.com/google/cel-go/common/types/ref"
"github.com/google/cel-go/common/types/traits"
"github.com/google/cel-go/interpreter"
"github.com/google/cel-go/parser"
)
var comparableTypes = []*cel.Type{
cel.IntType,
cel.UintType,
cel.DoubleType,
cel.BoolType,
cel.DurationType,
cel.TimestampType,
cel.StringType,
cel.BytesType,
}
// Lists returns a cel.EnvOption to configure extended functions for list manipulation.
// As a general note, all indices are zero-based.
//
// # Distinct
//
// Introduced in version: 2 (cost support in version 3)
//
// Returns the distinct elements of a list.
//
// <list(T)>.distinct() -> <list(T)>
//
// Examples:
//
// [1, 2, 2, 3, 3, 3].distinct() // return [1, 2, 3]
// ["b", "b", "c", "a", "c"].distinct() // return ["b", "c", "a"]
// [1, "b", 2, "b"].distinct() // return [1, "b", 2]
//
// # Range
//
// Introduced in version: 2 (cost support in version 3)
//
// Returns a list of integers from 0 to n-1.
//
// lists.range(<int>) -> <list(int)>
//
// Examples:
//
// lists.range(5) -> [0, 1, 2, 3, 4]
//
// # Reverse
//
// Introduced in version: 2 (cost support in version 3)
//
// Returns the elements of a list in reverse order.
//
// <list(T)>.reverse() -> <list(T)>
//
// Examples:
//
// [5, 3, 1, 2].reverse() // return [2, 1, 3, 5]
//
// # Slice
//
// Introduced in version: 0 (cost support in version 3)
//
// Returns a new sub-list using the indexes provided.
//
// <list>.slice(<int>, <int>) -> <list>
//
// Examples:
//
// [1,2,3,4].slice(1, 3) // return [2, 3]
// [1,2,3,4].slice(2, 4) // return [3 ,4]
//
// # Flatten
//
// Introduced in version: 1 (cost support in version 3)
//
// Flattens a list recursively.
// If an optional depth is provided, the list is flattened to a the specified level.
// A negative depth value will result in an error.
//
// <list>.flatten() -> <list>
// <list>.flatten(<int>) -> <list>
//
// Examples:
//
// [1,[2,3],[4]].flatten() // return [1, 2, 3, 4]
// [1,[2,[3,4]]].flatten() // return [1, 2, [3, 4]]
// [1,2,[],[],[3,4]].flatten() // return [1, 2, 3, 4]
// [1,[2,[3,[4]]]].flatten(2) // return [1, 2, 3, [4]]
// [1,[2,[3,[4]]]].flatten(-1) // error
//
// # Sort
//
// Introduced in version: 2 (cost support in version 3)
//
// Sorts a list with comparable elements. If the element type is not comparable
// or the element types are not the same, the function will produce an error.
//
// <list(T)>.sort() -> <list(T)>
// T in {int, uint, double, bool, duration, timestamp, string, bytes}
//
// Examples:
//
// [3, 2, 1].sort() // return [1, 2, 3]
// ["b", "c", "a"].sort() // return ["a", "b", "c"]
// [1, "b"].sort() // error
// [[1, 2, 3]].sort() // error
//
// # SortBy
//
// Introduced in version: 2 (cost support in version 3)
//
// Sorts a list by a key value, i.e., the order is determined by the result of
// an expression applied to each element of the list.
// The output of the key expression must be a comparable type, otherwise the
// function will return an error.
//
// <list(T)>.sortBy(<bindingName>, <keyExpr>) -> <list(T)>
// keyExpr returns a value in {int, uint, double, bool, duration, timestamp, string, bytes}
//
// Examples:
//
// [
// Player { name: "foo", score: 0 },
// Player { name: "bar", score: -10 },
// Player { name: "baz", score: 1000 },
// ].sortBy(e, e.score).map(e, e.name)
// == ["bar", "foo", "baz"]
func Lists(options ...ListsOption) cel.EnvOption {
l := &listsLib{version: math.MaxUint32}
for _, o := range options {
l = o(l)
}
return cel.Lib(l)
}
type listsLib struct {
version uint32
}
// LibraryName implements the SingletonLibrary interface method.
func (listsLib) LibraryName() string {
return "cel.lib.ext.lists"
}
// ListsOption is a functional interface for configuring the strings library.
type ListsOption func(*listsLib) *listsLib
// ListsVersion configures the version of the string library.
//
// The version limits which functions are available. Only functions introduced
// below or equal to the given version included in the library. If this option
// is not set, all functions are available.
//
// See the library documentation to determine which version a function was introduced.
// If the documentation does not state which version a function was introduced, it can
// be assumed to be introduced at version 0, when the library was first created.
func ListsVersion(version uint32) ListsOption {
return func(lib *listsLib) *listsLib {
lib.version = version
return lib
}
}
// CompileOptions implements the Library interface method.
func (lib listsLib) CompileOptions() []cel.EnvOption {
listType := cel.ListType(cel.TypeParamType("T"))
listListType := cel.ListType(listType)
listDyn := cel.ListType(cel.DynType)
opts := []cel.EnvOption{
cel.Function("slice",
cel.MemberOverload("list_slice",
[]*cel.Type{listType, cel.IntType, cel.IntType}, listType,
cel.FunctionBinding(func(args ...ref.Val) ref.Val {
list := args[0].(traits.Lister)
start := args[1].(types.Int)
end := args[2].(types.Int)
result, err := slice(list, start, end)
if err != nil {
return types.WrapErr(err)
}
return result
}),
),
),
}
if lib.version >= 1 {
opts = append(opts,
cel.Function("flatten",
cel.MemberOverload("list_flatten",
[]*cel.Type{listListType}, listType,
cel.UnaryBinding(func(arg ref.Val) ref.Val {
// double-check as type-guards disabled
list, ok := arg.(traits.Lister)
if !ok {
return types.ValOrErr(arg, "no such overload: %v.flatten()", arg.Type())
}
flatList, err := flatten(list, 1)
if err != nil {
return types.WrapErr(err)
}
return types.DefaultTypeAdapter.NativeToValue(flatList)
}),
),
cel.MemberOverload("list_flatten_int",
[]*cel.Type{listDyn, types.IntType}, listDyn,
cel.BinaryBinding(func(arg1, arg2 ref.Val) ref.Val {
// double-check as type-guards disabled
list, ok := arg1.(traits.Lister)
if !ok {
return types.ValOrErr(arg1, "no such overload: %v.flatten(%v)", arg1.Type(), arg2.Type())
}
depth, ok := arg2.(types.Int)
if !ok {
return types.ValOrErr(arg1, "no such overload: %v.flatten(%v)", arg1.Type(), arg2.Type())
}
flatList, err := flatten(list, int64(depth))
if err != nil {
return types.WrapErr(err)
}
return types.DefaultTypeAdapter.NativeToValue(flatList)
}),
),
// To handle the case where a variable of just `list(T)` is provided at runtime
// with a graceful failure more, disable the type guards since the implementation
// can handle lists which are already flat.
decls.DisableTypeGuards(true),
),
)
}
if lib.version >= 2 {
sortDecl := cel.Function("sort",
append(
templatedOverloads(comparableTypes, func(t *cel.Type) cel.FunctionOpt {
return cel.MemberOverload(
fmt.Sprintf("list_%s_sort", t.TypeName()),
[]*cel.Type{cel.ListType(t)}, cel.ListType(t),
)
}),
cel.SingletonUnaryBinding(
func(arg ref.Val) ref.Val {
// validated by type-guards
list := arg.(traits.Lister)
sorted, err := sortList(list)
if err != nil {
return types.WrapErr(err)
}
return sorted
},
// List traits
traits.ListerType,
),
)...,
)
opts = append(opts, sortDecl)
opts = append(opts, cel.Macros(cel.ReceiverMacro("sortBy", 2, sortByMacro)))
opts = append(opts, cel.Function("@sortByAssociatedKeys",
append(
templatedOverloads(comparableTypes, func(u *cel.Type) cel.FunctionOpt {
return cel.MemberOverload(
fmt.Sprintf("list_%s_sortByAssociatedKeys", u.TypeName()),
[]*cel.Type{listType, cel.ListType(u)}, listType,
)
}),
cel.SingletonBinaryBinding(
func(arg1, arg2 ref.Val) ref.Val {
// validated by type-guards
list := arg1.(traits.Lister)
keys := arg2.(traits.Lister)
sorted, err := sortListByAssociatedKeys(list, keys)
if err != nil {
return types.WrapErr(err)
}
return sorted
},
// List traits
traits.ListerType,
),
)...,
))
opts = append(opts, cel.Function("lists.range",
cel.Overload("lists_range",
[]*cel.Type{cel.IntType}, cel.ListType(cel.IntType),
cel.UnaryBinding(func(n ref.Val) ref.Val {
result, err := genRange(n.(types.Int))
if err != nil {
return types.WrapErr(err)
}
return result
}),
),
))
opts = append(opts, cel.Function("reverse",
cel.MemberOverload("list_reverse",
[]*cel.Type{listType}, listType,
cel.UnaryBinding(func(list ref.Val) ref.Val {
result, err := reverseList(list.(traits.Lister))
if err != nil {
return types.WrapErr(err)
}
return result
}),
),
))
opts = append(opts, cel.Function("distinct",
cel.MemberOverload("list_distinct",
[]*cel.Type{listType}, listType,
cel.UnaryBinding(func(list ref.Val) ref.Val {
result, err := distinctList(list.(traits.Lister))
if err != nil {
return types.WrapErr(err)
}
return result
}),
),
))
}
if lib.version >= 3 {
estimators := []checker.CostOption{
checker.OverloadCostEstimate("list_slice", estimateListSlice),
checker.OverloadCostEstimate("list_flatten", estimateListFlatten),
checker.OverloadCostEstimate("list_flatten_int", estimateListFlatten),
checker.OverloadCostEstimate("lists_range", estimateListsRange),
checker.OverloadCostEstimate("list_reverse", estimateListReverse),
checker.OverloadCostEstimate("list_distinct", estimateListDistinct),
}
for _, t := range comparableTypes {
estimators = append(estimators,
checker.OverloadCostEstimate(
fmt.Sprintf("list_%s_sort", t.TypeName()),
estimateListSort(t),
),
checker.OverloadCostEstimate(
fmt.Sprintf("list_%s_sortByAssociatedKeys", t.TypeName()),
estimateListSortBy(t),
),
)
}
opts = append(opts, cel.CostEstimatorOptions(estimators...))
}
return opts
}
// ProgramOptions implements the Library interface method.
func (lib *listsLib) ProgramOptions() []cel.ProgramOption {
var opts []cel.ProgramOption
if lib.version >= 3 {
// TODO: Add cost trackers for list operations
trackers := []interpreter.CostTrackerOption{
interpreter.OverloadCostTracker("list_slice", trackListOutputSize),
interpreter.OverloadCostTracker("list_flatten", trackListFlatten),
interpreter.OverloadCostTracker("list_flatten_int", trackListFlatten),
interpreter.OverloadCostTracker("lists_range", trackListOutputSize),
interpreter.OverloadCostTracker("list_reverse", trackListOutputSize),
interpreter.OverloadCostTracker("list_distinct", trackListDistinct),
}
for _, t := range comparableTypes {
trackers = append(trackers,
interpreter.OverloadCostTracker(
fmt.Sprintf("list_%s_sort", t.TypeName()),
trackListSort,
),
interpreter.OverloadCostTracker(
fmt.Sprintf("list_%s_sortByAssociatedKeys", t.TypeName()),
trackListSortBy,
),
)
}
opts = append(opts, cel.CostTrackerOptions(trackers...))
}
return opts
}
func genRange(n types.Int) (ref.Val, error) {
var newList []ref.Val
for i := types.Int(0); i < n; i++ {
newList = append(newList, i)
}
return types.DefaultTypeAdapter.NativeToValue(newList), nil
}
func reverseList(list traits.Lister) (ref.Val, error) {
var newList []ref.Val
listLength := list.Size().(types.Int)
for i := types.Int(0); i < listLength; i++ {
val := list.Get(listLength - i - 1)
newList = append(newList, val)
}
return types.DefaultTypeAdapter.NativeToValue(newList), nil
}
func slice(list traits.Lister, start, end types.Int) (ref.Val, error) {
listLength := list.Size().(types.Int)
if start < 0 || end < 0 {
return nil, fmt.Errorf("cannot slice(%d, %d), negative indexes not supported", start, end)
}
if start > end {
return nil, fmt.Errorf("cannot slice(%d, %d), start index must be less than or equal to end index", start, end)
}
if listLength < end {
return nil, fmt.Errorf("cannot slice(%d, %d), list is length %d", start, end, listLength)
}
var newList []ref.Val
for i := types.Int(start); i < end; i++ {
val := list.Get(i)
newList = append(newList, val)
}
return types.DefaultTypeAdapter.NativeToValue(newList), nil
}
func flatten(list traits.Lister, depth int64) ([]ref.Val, error) {
if depth < 0 {
return nil, fmt.Errorf("level must be non-negative")
}
var newList []ref.Val
iter := list.Iterator()
for iter.HasNext() == types.True {
val := iter.Next()
nestedList, isList := val.(traits.Lister)
if !isList || depth == 0 {
newList = append(newList, val)
continue
} else {
flattenedList, err := flatten(nestedList, depth-1)
if err != nil {
return nil, err
}
newList = append(newList, flattenedList...)
}
}
return newList, nil
}
func sortList(list traits.Lister) (ref.Val, error) {
return sortListByAssociatedKeys(list, list)
}
// Internal function used for the implementation of sort() and sortBy().
//
// Sorts a list of arbitrary elements, according to the order produced by sorting
// another list of comparable elements. If the element type of the keys is not
// comparable or the element types are not the same, the function will produce an error.
//
// <list(T)>.@sortByAssociatedKeys(<list(U)>) -> <list(T)>
// U in {int, uint, double, bool, duration, timestamp, string, bytes}
//
// Example:
//
// ["foo", "bar", "baz"].@sortByAssociatedKeys([3, 1, 2]) // return ["bar", "baz", "foo"]
func sortListByAssociatedKeys(list, keys traits.Lister) (ref.Val, error) {
listLength := list.Size().(types.Int)
keysLength := keys.Size().(types.Int)
if listLength != keysLength {
return nil, fmt.Errorf(
"@sortByAssociatedKeys() expected a list of the same size as the associated keys list, but got %d and %d elements respectively",
listLength,
keysLength,
)
}
if listLength == 0 {
return list, nil
}
elem := keys.Get(types.IntZero)
if _, ok := elem.(traits.Comparer); !ok {
return nil, fmt.Errorf("list elements must be comparable")
}
sortedIndices := make([]ref.Val, 0, listLength)
for i := types.IntZero; i < listLength; i++ {
sortedIndices = append(sortedIndices, i)
}
var err error
sort.Slice(sortedIndices, func(i, j int) bool {
iKey := keys.Get(sortedIndices[i])
jKey := keys.Get(sortedIndices[j])
if iKey.Type() != elem.Type() || jKey.Type() != elem.Type() {
err = fmt.Errorf("list elements must have the same type")
return false
}
return iKey.(traits.Comparer).Compare(jKey) == types.IntNegOne
})
if err != nil {
return nil, err
}
sorted := make([]ref.Val, 0, listLength)
for _, sortedIdx := range sortedIndices {
sorted = append(sorted, list.Get(sortedIdx))
}
return types.DefaultTypeAdapter.NativeToValue(sorted), nil
}
// sortByMacro transforms an expression like:
//
// mylistExpr.sortBy(e, -math.abs(e))
//
// into something equivalent to:
//
// cel.bind(
// __sortBy_input__,
// myListExpr,
// __sortBy_input__.@sortByAssociatedKeys(__sortBy_input__.map(e, -math.abs(e))
// )
func sortByMacro(meh cel.MacroExprFactory, target ast.Expr, args []ast.Expr) (ast.Expr, *cel.Error) {
varIdent := meh.NewIdent("@__sortBy_input__")
varName := varIdent.AsIdent()
targetKind := target.Kind()
if targetKind != ast.ListKind &&
targetKind != ast.SelectKind &&
targetKind != ast.IdentKind &&
targetKind != ast.ComprehensionKind &&
targetKind != ast.CallKind {
return nil, meh.NewError(target.ID(), "sortBy can only be applied to a list, identifier, comprehension, call or select expression")
}
mapCompr, err := parser.MakeMap(meh, meh.Copy(varIdent), args)
if err != nil {
return nil, err
}
callExpr := meh.NewMemberCall("@sortByAssociatedKeys",
meh.Copy(varIdent),
mapCompr,
)
bindExpr := meh.NewComprehension(
meh.NewList(),
"#unused",
varName,
target,
meh.NewLiteral(types.False),
varIdent,
callExpr,
)
return bindExpr, nil
}
func distinctList(list traits.Lister) (ref.Val, error) {
listLength := list.Size().(types.Int)
if listLength == 0 {
return list, nil
}
uniqueList := make([]ref.Val, 0, listLength)
for i := types.IntZero; i < listLength; i++ {
val := list.Get(i)
seen := false
for j := types.IntZero; j < types.Int(len(uniqueList)); j++ {
if i == j {
continue
}
other := uniqueList[j]
if val.Equal(other) == types.True {
seen = true
break
}
}
if !seen {
uniqueList = append(uniqueList, val)
}
}
return types.DefaultTypeAdapter.NativeToValue(uniqueList), nil
}
func templatedOverloads(types []*cel.Type, template func(t *cel.Type) cel.FunctionOpt) []cel.FunctionOpt {
overloads := make([]cel.FunctionOpt, len(types))
for i, t := range types {
overloads[i] = template(t)
}
return overloads
}
// estimateListSlice computes an O(n) slice operation with a cost factor of 1.
func estimateListSlice(estimator checker.CostEstimator, target *checker.AstNode, args []checker.AstNode) *checker.CallEstimate {
if target == nil || len(args) != 2 {
return nil
}
sz := estimateSize(estimator, *target)
start := nodeAsIntValue(args[0], 0)
end := nodeAsIntValue(args[1], sz.Max)
return estimateAllocatingListCall(1, checker.FixedSizeEstimate(end-start))
}
// estimateListsRange computes an O(n) range operation with a cost factor of 1.
func estimateListsRange(estimator checker.CostEstimator, target *checker.AstNode, args []checker.AstNode) *checker.CallEstimate {
if target != nil || len(args) != 1 {
return nil
}
return estimateAllocatingListCall(1, checker.FixedSizeEstimate(nodeAsIntValue(args[0], math.MaxUint)))
}
// estimateListReverse computes an O(n) reverse operation with a cost factor of 1.
func estimateListReverse(estimator checker.CostEstimator, target *checker.AstNode, args []checker.AstNode) *checker.CallEstimate {
if target == nil || len(args) != 0 {
return nil
}
return estimateAllocatingListCall(1, estimateSize(estimator, *target))
}
// estimateListFlatten computes an O(n) flatten operation with a cost factor proportional to the flatten depth.
func estimateListFlatten(estimator checker.CostEstimator, target *checker.AstNode, args []checker.AstNode) *checker.CallEstimate {
if target == nil || len(args) > 1 {
return nil
}
depth := uint64(1)
if len(args) == 1 {
depth = nodeAsIntValue(args[0], math.MaxUint)
}
return estimateAllocatingListCall(float64(depth), estimateSize(estimator, *target))
}
// Compute an O(n^2) with a cost factor of 2, equivalent to sets.contains with a result list
// which can vary in size from 1 element to the original list size.
func estimateListDistinct(estimator checker.CostEstimator, target *checker.AstNode, args []checker.AstNode) *checker.CallEstimate {
if target == nil || len(args) != 0 {
return nil
}
sz := estimateSize(estimator, *target)
costFactor := 2.0
return estimateAllocatingListCall(costFactor, sz.Multiply(sz))
}
// estimateListSort computes an O(n^2) sort operation with a cost factor of 2 for the equality
// operations against the elements in the list against themselves which occur during the sort computation.
func estimateListSort(t *types.Type) checker.FunctionEstimator {
return func(estimator checker.CostEstimator, target *checker.AstNode, args []checker.AstNode) *checker.CallEstimate {
if target == nil || len(args) != 0 {
return nil
}
return estimateListSortCost(estimator, *target, t)
}
}
// estimateListSortBy computes an O(n^2) sort operation with a cost factor of 2 for the equality
// operations against the sort index list which occur during the sort computation.
func estimateListSortBy(u *types.Type) checker.FunctionEstimator {
return func(estimator checker.CostEstimator, target *checker.AstNode, args []checker.AstNode) *checker.CallEstimate {
if target == nil || len(args) != 1 {
return nil
}
// Estimate the size of the list used as the sort index
return estimateListSortCost(estimator, args[0], u)
}
}
// estimateListSortCost estimates an O(n^2) sort operation with a cost factor of 2 for the equality
// operations which occur during the sort computation.
func estimateListSortCost(estimator checker.CostEstimator, node checker.AstNode, elemType *types.Type) *checker.CallEstimate {
sz := estimateSize(estimator, node)
costFactor := 2.0
switch elemType {
case types.StringType, types.BytesType:
costFactor += common.StringTraversalCostFactor
}
return estimateAllocatingListCall(costFactor, sz.Multiply(sz))
}
// estimateAllocatingListCall computes cost as a function of the size of the result list with a
// baseline cost for the call dispatch and the associated list allocation.
func estimateAllocatingListCall(costFactor float64, listSize checker.SizeEstimate) *checker.CallEstimate {
return estimateListCall(costFactor, listSize, true)
}
// estimateListCall computes cost as a function of the size of the target list and whether the
// call allocates memory.
func estimateListCall(costFactor float64, listSize checker.SizeEstimate, allocates bool) *checker.CallEstimate {
cost := listSize.MultiplyByCostFactor(costFactor).Add(callCostEstimate)
if allocates {
cost = cost.Add(checker.FixedCostEstimate(common.ListCreateBaseCost))
}
return &checker.CallEstimate{CostEstimate: cost, ResultSize: &listSize}
}
// trackListOutputSize computes cost as a function of the size of the result list.
func trackListOutputSize(_ []ref.Val, result ref.Val) *uint64 {
return trackAllocatingListCall(1, actualSize(result))
}
// trackListFlatten computes cost as a function of the size of the result list and the depth of
// the flatten operation.
func trackListFlatten(args []ref.Val, _ ref.Val) *uint64 {
depth := 1.0
if len(args) == 2 {
depth = float64(args[1].(types.Int))
}
inputSize := actualSize(args[0])
return trackAllocatingListCall(depth, inputSize)
}
// trackListDistinct computes costs as a worst-case O(n^2) operation over the input list.
func trackListDistinct(args []ref.Val, _ ref.Val) *uint64 {
return trackListSelfCompare(args[0].(traits.Lister))
}
// trackListSort computes costs as a worst-case O(n^2) operation over the input list.
func trackListSort(args []ref.Val, result ref.Val) *uint64 {
return trackListSelfCompare(args[0].(traits.Lister))
}
// trackListSortBy computes costs as a worst-case O(n^2) operation over the sort index list.
func trackListSortBy(args []ref.Val, result ref.Val) *uint64 {
return trackListSelfCompare(args[1].(traits.Lister))
}
// trackListSelfCompare computes costs as a worst-case O(n^2) operation over the input list.
func trackListSelfCompare(l traits.Lister) *uint64 {
sz := actualSize(l)
costFactor := 2.0
if sz == 0 {
return trackAllocatingListCall(costFactor, 0)
}
elem := l.Get(types.IntZero)
if elem.Type() == types.StringType || elem.Type() == types.BytesType {
costFactor += common.StringTraversalCostFactor
}
return trackAllocatingListCall(costFactor, sz*sz)
}
// trackAllocatingListCall computes costs as a function of the size of the result list with a baseline cost
// for the call dispatch and the associated list allocation.
func trackAllocatingListCall(costFactor float64, size uint64) *uint64 {
cost := uint64(float64(size)*costFactor) + callCost + common.ListCreateBaseCost
return &cost
}
func nodeAsIntValue(node checker.AstNode, defaultVal uint64) uint64 {
if node.Expr().Kind() != ast.LiteralKind {
return defaultVal
}
lit := node.Expr().AsLiteral()
if lit.Type() != types.IntType {
return defaultVal
}
val := lit.(types.Int)
if val < types.IntZero {
return 0
}
return uint64(lit.(types.Int))
}
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package ext
import (
"fmt"
"math"
"strings"
"github.com/google/cel-go/cel"
"github.com/google/cel-go/common/ast"
"github.com/google/cel-go/common/types"
"github.com/google/cel-go/common/types/ref"
"github.com/google/cel-go/common/types/traits"
)
// Math returns a cel.EnvOption to configure namespaced math helper macros and
// functions.
//
// Note, all macros use the 'math' namespace; however, at the time of macro
// expansion the namespace looks just like any other identifier. If you are
// currently using a variable named 'math', the macro will likely work just as
// intended; however, there is some chance for collision.
//
// # Math.Greatest
//
// Returns the greatest valued number present in the arguments to the macro.
//
// Greatest is a variable argument count macro which must take at least one
// argument. Simple numeric and list literals are supported as valid argument
// types; however, other literals will be flagged as errors during macro
// expansion. If the argument expression does not resolve to a numeric or
// list(numeric) type during type-checking, or during runtime then an error
// will be produced. If a list argument is empty, this too will produce an
// error.
//
// math.greatest(<arg>, ...) -> <double|int|uint>
//
// Examples:
//
// math.greatest(1) // 1
// math.greatest(1u, 2u) // 2u
// math.greatest(-42.0, -21.5, -100.0) // -21.5
// math.greatest([-42.0, -21.5, -100.0]) // -21.5
// math.greatest(numbers) // numbers must be list(numeric)
//
// math.greatest() // parse error
// math.greatest('string') // parse error
// math.greatest(a, b) // check-time error if a or b is non-numeric
// math.greatest(dyn('string')) // runtime error
//
// # Math.Least
//
// Returns the least valued number present in the arguments to the macro.
//
// Least is a variable argument count macro which must take at least one
// argument. Simple numeric and list literals are supported as valid argument
// types; however, other literals will be flagged as errors during macro
// expansion. If the argument expression does not resolve to a numeric or
// list(numeric) type during type-checking, or during runtime then an error
// will be produced. If a list argument is empty, this too will produce an
// error.
//
// math.least(<arg>, ...) -> <double|int|uint>
//
// Examples:
//
// math.least(1) // 1
// math.least(1u, 2u) // 1u
// math.least(-42.0, -21.5, -100.0) // -100.0
// math.least([-42.0, -21.5, -100.0]) // -100.0
// math.least(numbers) // numbers must be list(numeric)
//
// math.least() // parse error
// math.least('string') // parse error
// math.least(a, b) // check-time error if a or b is non-numeric
// math.least(dyn('string')) // runtime error
//
// # Math.BitOr
//
// Introduced at version: 1
//
// Performs a bitwise-OR operation over two int or uint values.
//
// math.bitOr(<int>, <int>) -> <int>
// math.bitOr(<uint>, <uint>) -> <uint>
//
// Examples:
//
// math.bitOr(1u, 2u) // returns 3u
// math.bitOr(-2, -4) // returns -2
//
// # Math.BitAnd
//
// Introduced at version: 1
//
// Performs a bitwise-AND operation over two int or uint values.
//
// math.bitAnd(<int>, <int>) -> <int>
// math.bitAnd(<uint>, <uint>) -> <uint>
//
// Examples:
//
// math.bitAnd(3u, 2u) // return 2u
// math.bitAnd(3, 5) // returns 3
// math.bitAnd(-3, -5) // returns -7
//
// # Math.BitXor
//
// Introduced at version: 1
//
// math.bitXor(<int>, <int>) -> <int>
// math.bitXor(<uint>, <uint>) -> <uint>
//
// Performs a bitwise-XOR operation over two int or uint values.
//
// Examples:
//
// math.bitXor(3u, 5u) // returns 6u
// math.bitXor(1, 3) // returns 2
//
// # Math.BitNot
//
// Introduced at version: 1
//
// Function which accepts a single int or uint and performs a bitwise-NOT
// ones-complement of the given binary value.
//
// math.bitNot(<int>) -> <int>
// math.bitNot(<uint>) -> <uint>
//
// Examples
//
// math.bitNot(1) // returns -1
// math.bitNot(-1) // return 0
// math.bitNot(0u) // returns 18446744073709551615u
//
// # Math.BitShiftLeft
//
// Introduced at version: 1
//
// Perform a left shift of bits on the first parameter, by the amount of bits
// specified in the second parameter. The first parameter is either a uint or
// an int. The second parameter must be an int.
//
// When the second parameter is 64 or greater, 0 will be always be returned
// since the number of bits shifted is greater than or equal to the total bit
// length of the number being shifted. Negative valued bit shifts will result
// in a runtime error.
//
// math.bitShiftLeft(<int>, <int>) -> <int>
// math.bitShiftLeft(<uint>, <int>) -> <uint>
//
// Examples
//
// math.bitShiftLeft(1, 2) // returns 4
// math.bitShiftLeft(-1, 2) // returns -4
// math.bitShiftLeft(1u, 2) // return 4u
// math.bitShiftLeft(1u, 200) // returns 0u
//
// # Math.BitShiftRight
//
// Introduced at version: 1
//
// Perform a right shift of bits on the first parameter, by the amount of bits
// specified in the second parameter. The first parameter is either a uint or
// an int. The second parameter must be an int.
//
// When the second parameter is 64 or greater, 0 will always be returned since
// the number of bits shifted is greater than or equal to the total bit length
// of the number being shifted. Negative valued bit shifts will result in a
// runtime error.
//
// The sign bit extension will not be preserved for this operation: vacant bits
// on the left are filled with 0.
//
// math.bitShiftRight(<int>, <int>) -> <int>
// math.bitShiftRight(<uint>, <int>) -> <uint>
//
// Examples
//
// math.bitShiftRight(1024, 2) // returns 256
// math.bitShiftRight(1024u, 2) // returns 256u
// math.bitShiftRight(1024u, 64) // returns 0u
//
// # Math.Ceil
//
// Introduced at version: 1
//
// Compute the ceiling of a double value.
//
// math.ceil(<double>) -> <double>
//
// Examples:
//
// math.ceil(1.2) // returns 2.0
// math.ceil(-1.2) // returns -1.0
//
// # Math.Floor
//
// Introduced at version: 1
//
// Compute the floor of a double value.
//
// math.floor(<double>) -> <double>
//
// Examples:
//
// math.floor(1.2) // returns 1.0
// math.floor(-1.2) // returns -2.0
//
// # Math.Round
//
// Introduced at version: 1
//
// Rounds the double value to the nearest whole number with ties rounding away
// from zero, e.g. 1.5 -> 2.0, -1.5 -> -2.0.
//
// math.round(<double>) -> <double>
//
// Examples:
//
// math.round(1.2) // returns 1.0
// math.round(1.5) // returns 2.0
// math.round(-1.5) // returns -2.0
//
// # Math.Trunc
//
// Introduced at version: 1
//
// Truncates the fractional portion of the double value.
//
// math.trunc(<double>) -> <double>
//
// Examples:
//
// math.trunc(-1.3) // returns -1.0
// math.trunc(1.3) // returns 1.0
//
// # Math.Abs
//
// Introduced at version: 1
//
// Returns the absolute value of the numeric type provided as input. If the
// value is NaN, the output is NaN. If the input is int64 min, the function
// will result in an overflow error.
//
// math.abs(<double>) -> <double>
// math.abs(<int>) -> <int>
// math.abs(<uint>) -> <uint>
//
// Examples:
//
// math.abs(-1) // returns 1
// math.abs(1) // returns 1
// math.abs(-9223372036854775808) // overflow error
//
// # Math.Sign
//
// Introduced at version: 1
//
// Returns the sign of the numeric type, either -1, 0, 1 as an int, double, or
// uint depending on the overload. For floating point values, if NaN is
// provided as input, the output is also NaN. The implementation does not
// differentiate between positive and negative zero.
//
// math.sign(<double>) -> <double>
// math.sign(<int>) -> <int>
// math.sign(<uint>) -> <uint>
//
// Examples:
//
// math.sign(-42) // returns -1
// math.sign(0) // returns 0
// math.sign(42) // returns 1
//
// # Math.IsInf
//
// Introduced at version: 1
//
// Returns true if the input double value is -Inf or +Inf.
//
// math.isInf(<double>) -> <bool>
//
// Examples:
//
// math.isInf(1.0/0.0) // returns true
// math.isInf(1.2) // returns false
//
// # Math.IsNaN
//
// Introduced at version: 1
//
// Returns true if the input double value is NaN, false otherwise.
//
// math.isNaN(<double>) -> <bool>
//
// Examples:
//
// math.isNaN(0.0/0.0) // returns true
// math.isNaN(1.2) // returns false
//
// # Math.IsFinite
//
// Introduced at version: 1
//
// Returns true if the value is a finite number. Equivalent in behavior to:
// !math.isNaN(double) && !math.isInf(double)
//
// math.isFinite(<double>) -> <bool>
//
// Examples:
//
// math.isFinite(0.0/0.0) // returns false
// math.isFinite(1.2) // returns true
//
// # Math.Sqrt
//
// Introduced at version: 2
//
// Returns the square root of the given input as double
// Throws error for negative or non-numeric inputs
//
// math.sqrt(<double>) -> <double>
// math.sqrt(<int>) -> <double>
// math.sqrt(<uint>) -> <double>
//
// Examples:
//
// math.sqrt(81) // returns 9.0
// math.sqrt(985.25) // returns 31.388692231439016
// math.sqrt(-15) // returns NaN
func Math(options ...MathOption) cel.EnvOption {
m := &mathLib{version: math.MaxUint32}
for _, o := range options {
m = o(m)
}
return cel.Lib(m)
}
const (
mathNamespace = "math"
leastMacro = "least"
greatestMacro = "greatest"
// Min-max functions
minFunc = "math.@min"
maxFunc = "math.@max"
// Rounding functions
ceilFunc = "math.ceil"
floorFunc = "math.floor"
roundFunc = "math.round"
truncFunc = "math.trunc"
// Floating point helper functions
isInfFunc = "math.isInf"
isNanFunc = "math.isNaN"
isFiniteFunc = "math.isFinite"
// Signedness functions
absFunc = "math.abs"
signFunc = "math.sign"
// SquareRoot function
sqrtFunc = "math.sqrt"
// Bitwise functions
bitAndFunc = "math.bitAnd"
bitOrFunc = "math.bitOr"
bitXorFunc = "math.bitXor"
bitNotFunc = "math.bitNot"
bitShiftLeftFunc = "math.bitShiftLeft"
bitShiftRightFunc = "math.bitShiftRight"
)
var (
errIntOverflow = types.NewErr("integer overflow")
)
// MathOption declares a functional operator for configuring math extensions.
type MathOption func(*mathLib) *mathLib
// MathVersion sets the library version for math extensions.
func MathVersion(version uint32) MathOption {
return func(lib *mathLib) *mathLib {
lib.version = version
return lib
}
}
type mathLib struct {
version uint32
}
// LibraryName implements the SingletonLibrary interface method.
func (*mathLib) LibraryName() string {
return "cel.lib.ext.math"
}
// CompileOptions implements the Library interface method.
func (lib *mathLib) CompileOptions() []cel.EnvOption {
opts := []cel.EnvOption{
cel.Macros(
// math.least(num, ...)
cel.ReceiverVarArgMacro(leastMacro, mathLeast),
// math.greatest(num, ...)
cel.ReceiverVarArgMacro(greatestMacro, mathGreatest),
),
cel.Function(minFunc,
cel.Overload("math_@min_double", []*cel.Type{cel.DoubleType}, cel.DoubleType,
cel.UnaryBinding(identity)),
cel.Overload("math_@min_int", []*cel.Type{cel.IntType}, cel.IntType,
cel.UnaryBinding(identity)),
cel.Overload("math_@min_uint", []*cel.Type{cel.UintType}, cel.UintType,
cel.UnaryBinding(identity)),
cel.Overload("math_@min_double_double", []*cel.Type{cel.DoubleType, cel.DoubleType}, cel.DoubleType,
cel.BinaryBinding(minPair)),
cel.Overload("math_@min_int_int", []*cel.Type{cel.IntType, cel.IntType}, cel.IntType,
cel.BinaryBinding(minPair)),
cel.Overload("math_@min_uint_uint", []*cel.Type{cel.UintType, cel.UintType}, cel.UintType,
cel.BinaryBinding(minPair)),
cel.Overload("math_@min_int_uint", []*cel.Type{cel.IntType, cel.UintType}, cel.DynType,
cel.BinaryBinding(minPair)),
cel.Overload("math_@min_int_double", []*cel.Type{cel.IntType, cel.DoubleType}, cel.DynType,
cel.BinaryBinding(minPair)),
cel.Overload("math_@min_double_int", []*cel.Type{cel.DoubleType, cel.IntType}, cel.DynType,
cel.BinaryBinding(minPair)),
cel.Overload("math_@min_double_uint", []*cel.Type{cel.DoubleType, cel.UintType}, cel.DynType,
cel.BinaryBinding(minPair)),
cel.Overload("math_@min_uint_int", []*cel.Type{cel.UintType, cel.IntType}, cel.DynType,
cel.BinaryBinding(minPair)),
cel.Overload("math_@min_uint_double", []*cel.Type{cel.UintType, cel.DoubleType}, cel.DynType,
cel.BinaryBinding(minPair)),
cel.Overload("math_@min_list_double", []*cel.Type{cel.ListType(cel.DoubleType)}, cel.DoubleType,
cel.UnaryBinding(minList)),
cel.Overload("math_@min_list_int", []*cel.Type{cel.ListType(cel.IntType)}, cel.IntType,
cel.UnaryBinding(minList)),
cel.Overload("math_@min_list_uint", []*cel.Type{cel.ListType(cel.UintType)}, cel.UintType,
cel.UnaryBinding(minList)),
),
cel.Function(maxFunc,
cel.Overload("math_@max_double", []*cel.Type{cel.DoubleType}, cel.DoubleType,
cel.UnaryBinding(identity)),
cel.Overload("math_@max_int", []*cel.Type{cel.IntType}, cel.IntType,
cel.UnaryBinding(identity)),
cel.Overload("math_@max_uint", []*cel.Type{cel.UintType}, cel.UintType,
cel.UnaryBinding(identity)),
cel.Overload("math_@max_double_double", []*cel.Type{cel.DoubleType, cel.DoubleType}, cel.DoubleType,
cel.BinaryBinding(maxPair)),
cel.Overload("math_@max_int_int", []*cel.Type{cel.IntType, cel.IntType}, cel.IntType,
cel.BinaryBinding(maxPair)),
cel.Overload("math_@max_uint_uint", []*cel.Type{cel.UintType, cel.UintType}, cel.UintType,
cel.BinaryBinding(maxPair)),
cel.Overload("math_@max_int_uint", []*cel.Type{cel.IntType, cel.UintType}, cel.DynType,
cel.BinaryBinding(maxPair)),
cel.Overload("math_@max_int_double", []*cel.Type{cel.IntType, cel.DoubleType}, cel.DynType,
cel.BinaryBinding(maxPair)),
cel.Overload("math_@max_double_int", []*cel.Type{cel.DoubleType, cel.IntType}, cel.DynType,
cel.BinaryBinding(maxPair)),
cel.Overload("math_@max_double_uint", []*cel.Type{cel.DoubleType, cel.UintType}, cel.DynType,
cel.BinaryBinding(maxPair)),
cel.Overload("math_@max_uint_int", []*cel.Type{cel.UintType, cel.IntType}, cel.DynType,
cel.BinaryBinding(maxPair)),
cel.Overload("math_@max_uint_double", []*cel.Type{cel.UintType, cel.DoubleType}, cel.DynType,
cel.BinaryBinding(maxPair)),
cel.Overload("math_@max_list_double", []*cel.Type{cel.ListType(cel.DoubleType)}, cel.DoubleType,
cel.UnaryBinding(maxList)),
cel.Overload("math_@max_list_int", []*cel.Type{cel.ListType(cel.IntType)}, cel.IntType,
cel.UnaryBinding(maxList)),
cel.Overload("math_@max_list_uint", []*cel.Type{cel.ListType(cel.UintType)}, cel.UintType,
cel.UnaryBinding(maxList)),
),
}
if lib.version >= 1 {
opts = append(opts,
// Rounding function declarations
cel.Function(ceilFunc,
cel.Overload("math_ceil_double", []*cel.Type{cel.DoubleType}, cel.DoubleType,
cel.UnaryBinding(ceil))),
cel.Function(floorFunc,
cel.Overload("math_floor_double", []*cel.Type{cel.DoubleType}, cel.DoubleType,
cel.UnaryBinding(floor))),
cel.Function(roundFunc,
cel.Overload("math_round_double", []*cel.Type{cel.DoubleType}, cel.DoubleType,
cel.UnaryBinding(round))),
cel.Function(truncFunc,
cel.Overload("math_trunc_double", []*cel.Type{cel.DoubleType}, cel.DoubleType,
cel.UnaryBinding(trunc))),
// Floating point helpers
cel.Function(isInfFunc,
cel.Overload("math_isInf_double", []*cel.Type{cel.DoubleType}, cel.BoolType,
cel.UnaryBinding(isInf))),
cel.Function(isNanFunc,
cel.Overload("math_isNaN_double", []*cel.Type{cel.DoubleType}, cel.BoolType,
cel.UnaryBinding(isNaN))),
cel.Function(isFiniteFunc,
cel.Overload("math_isFinite_double", []*cel.Type{cel.DoubleType}, cel.BoolType,
cel.UnaryBinding(isFinite))),
// Signedness functions
cel.Function(absFunc,
cel.Overload("math_abs_double", []*cel.Type{cel.DoubleType}, cel.DoubleType,
cel.UnaryBinding(absDouble)),
cel.Overload("math_abs_int", []*cel.Type{cel.IntType}, cel.IntType,
cel.UnaryBinding(absInt)),
cel.Overload("math_abs_uint", []*cel.Type{cel.UintType}, cel.UintType,
cel.UnaryBinding(identity)),
),
cel.Function(signFunc,
cel.Overload("math_sign_double", []*cel.Type{cel.DoubleType}, cel.DoubleType,
cel.UnaryBinding(sign)),
cel.Overload("math_sign_int", []*cel.Type{cel.IntType}, cel.IntType,
cel.UnaryBinding(sign)),
cel.Overload("math_sign_uint", []*cel.Type{cel.UintType}, cel.UintType,
cel.UnaryBinding(sign)),
),
// Bitwise operator declarations
cel.Function(bitAndFunc,
cel.Overload("math_bitAnd_int_int", []*cel.Type{cel.IntType, cel.IntType}, cel.IntType,
cel.BinaryBinding(bitAndPairInt)),
cel.Overload("math_bitAnd_uint_uint", []*cel.Type{cel.UintType, cel.UintType}, cel.UintType,
cel.BinaryBinding(bitAndPairUint)),
),
cel.Function(bitOrFunc,
cel.Overload("math_bitOr_int_int", []*cel.Type{cel.IntType, cel.IntType}, cel.IntType,
cel.BinaryBinding(bitOrPairInt)),
cel.Overload("math_bitOr_uint_uint", []*cel.Type{cel.UintType, cel.UintType}, cel.UintType,
cel.BinaryBinding(bitOrPairUint)),
),
cel.Function(bitXorFunc,
cel.Overload("math_bitXor_int_int", []*cel.Type{cel.IntType, cel.IntType}, cel.IntType,
cel.BinaryBinding(bitXorPairInt)),
cel.Overload("math_bitXor_uint_uint", []*cel.Type{cel.UintType, cel.UintType}, cel.UintType,
cel.BinaryBinding(bitXorPairUint)),
),
cel.Function(bitNotFunc,
cel.Overload("math_bitNot_int_int", []*cel.Type{cel.IntType}, cel.IntType,
cel.UnaryBinding(bitNotInt)),
cel.Overload("math_bitNot_uint_uint", []*cel.Type{cel.UintType}, cel.UintType,
cel.UnaryBinding(bitNotUint)),
),
cel.Function(bitShiftLeftFunc,
cel.Overload("math_bitShiftLeft_int_int", []*cel.Type{cel.IntType, cel.IntType}, cel.IntType,
cel.BinaryBinding(bitShiftLeftIntInt)),
cel.Overload("math_bitShiftLeft_uint_int", []*cel.Type{cel.UintType, cel.IntType}, cel.UintType,
cel.BinaryBinding(bitShiftLeftUintInt)),
),
cel.Function(bitShiftRightFunc,
cel.Overload("math_bitShiftRight_int_int", []*cel.Type{cel.IntType, cel.IntType}, cel.IntType,
cel.BinaryBinding(bitShiftRightIntInt)),
cel.Overload("math_bitShiftRight_uint_int", []*cel.Type{cel.UintType, cel.IntType}, cel.UintType,
cel.BinaryBinding(bitShiftRightUintInt)),
),
)
}
if lib.version >= 2 {
opts = append(opts,
cel.Function(sqrtFunc,
cel.Overload("math_sqrt_double", []*cel.Type{cel.DoubleType}, cel.DoubleType,
cel.UnaryBinding(sqrt)),
cel.Overload("math_sqrt_int", []*cel.Type{cel.IntType}, cel.DoubleType,
cel.UnaryBinding(sqrt)),
cel.Overload("math_sqrt_uint", []*cel.Type{cel.UintType}, cel.DoubleType,
cel.UnaryBinding(sqrt)),
),
)
}
return opts
}
// ProgramOptions implements the Library interface method.
func (*mathLib) ProgramOptions() []cel.ProgramOption {
return []cel.ProgramOption{}
}
func mathLeast(meh cel.MacroExprFactory, target ast.Expr, args []ast.Expr) (ast.Expr, *cel.Error) {
if !macroTargetMatchesNamespace(mathNamespace, target) {
return nil, nil
}
switch len(args) {
case 0:
return nil, meh.NewError(target.ID(), "math.least() requires at least one argument")
case 1:
if isListLiteralWithNumericArgs(args[0]) || isNumericArgType(args[0]) {
return meh.NewCall(minFunc, args[0]), nil
}
return nil, meh.NewError(args[0].ID(), "math.least() invalid single argument value")
case 2:
err := checkInvalidArgs(meh, "math.least()", args)
if err != nil {
return nil, err
}
return meh.NewCall(minFunc, args...), nil
default:
err := checkInvalidArgs(meh, "math.least()", args)
if err != nil {
return nil, err
}
return meh.NewCall(minFunc, meh.NewList(args...)), nil
}
}
func mathGreatest(mef cel.MacroExprFactory, target ast.Expr, args []ast.Expr) (ast.Expr, *cel.Error) {
if !macroTargetMatchesNamespace(mathNamespace, target) {
return nil, nil
}
switch len(args) {
case 0:
return nil, mef.NewError(target.ID(), "math.greatest() requires at least one argument")
case 1:
if isListLiteralWithNumericArgs(args[0]) || isNumericArgType(args[0]) {
return mef.NewCall(maxFunc, args[0]), nil
}
return nil, mef.NewError(args[0].ID(), "math.greatest() invalid single argument value")
case 2:
err := checkInvalidArgs(mef, "math.greatest()", args)
if err != nil {
return nil, err
}
return mef.NewCall(maxFunc, args...), nil
default:
err := checkInvalidArgs(mef, "math.greatest()", args)
if err != nil {
return nil, err
}
return mef.NewCall(maxFunc, mef.NewList(args...)), nil
}
}
func identity(val ref.Val) ref.Val {
return val
}
func ceil(val ref.Val) ref.Val {
v := val.(types.Double)
return types.Double(math.Ceil(float64(v)))
}
func floor(val ref.Val) ref.Val {
v := val.(types.Double)
return types.Double(math.Floor(float64(v)))
}
func round(val ref.Val) ref.Val {
v := val.(types.Double)
return types.Double(math.Round(float64(v)))
}
func trunc(val ref.Val) ref.Val {
v := val.(types.Double)
return types.Double(math.Trunc(float64(v)))
}
func isInf(val ref.Val) ref.Val {
v := val.(types.Double)
return types.Bool(math.IsInf(float64(v), 0))
}
func isFinite(val ref.Val) ref.Val {
v := float64(val.(types.Double))
return types.Bool(!math.IsInf(v, 0) && !math.IsNaN(v))
}
func isNaN(val ref.Val) ref.Val {
v := val.(types.Double)
return types.Bool(math.IsNaN(float64(v)))
}
func absDouble(val ref.Val) ref.Val {
v := float64(val.(types.Double))
return types.Double(math.Abs(v))
}
func absInt(val ref.Val) ref.Val {
v := int64(val.(types.Int))
if v == math.MinInt64 {
return errIntOverflow
}
if v >= 0 {
return val
}
return -types.Int(v)
}
func sign(val ref.Val) ref.Val {
switch v := val.(type) {
case types.Double:
if isNaN(v) == types.True {
return v
}
zero := types.Double(0)
if v > zero {
return types.Double(1)
}
if v < zero {
return types.Double(-1)
}
return zero
case types.Int:
return v.Compare(types.IntZero)
case types.Uint:
if v == types.Uint(0) {
return types.Uint(0)
}
return types.Uint(1)
default:
return maybeSuffixError(val, "math.sign")
}
}
func sqrt(val ref.Val) ref.Val {
switch v := val.(type) {
case types.Double:
return types.Double(math.Sqrt(float64(v)))
case types.Int:
return types.Double(math.Sqrt(float64(v)))
case types.Uint:
return types.Double(math.Sqrt(float64(v)))
default:
return types.NewErr("no such overload: sqrt")
}
}
func bitAndPairInt(first, second ref.Val) ref.Val {
l := first.(types.Int)
r := second.(types.Int)
return l & r
}
func bitAndPairUint(first, second ref.Val) ref.Val {
l := first.(types.Uint)
r := second.(types.Uint)
return l & r
}
func bitOrPairInt(first, second ref.Val) ref.Val {
l := first.(types.Int)
r := second.(types.Int)
return l | r
}
func bitOrPairUint(first, second ref.Val) ref.Val {
l := first.(types.Uint)
r := second.(types.Uint)
return l | r
}
func bitXorPairInt(first, second ref.Val) ref.Val {
l := first.(types.Int)
r := second.(types.Int)
return l ^ r
}
func bitXorPairUint(first, second ref.Val) ref.Val {
l := first.(types.Uint)
r := second.(types.Uint)
return l ^ r
}
func bitNotInt(value ref.Val) ref.Val {
v := value.(types.Int)
return ^v
}
func bitNotUint(value ref.Val) ref.Val {
v := value.(types.Uint)
return ^v
}
func bitShiftLeftIntInt(value, bits ref.Val) ref.Val {
v := value.(types.Int)
bs := bits.(types.Int)
if bs < types.IntZero {
return types.NewErr("math.bitShiftLeft() negative offset: %d", bs)
}
return v << bs
}
func bitShiftLeftUintInt(value, bits ref.Val) ref.Val {
v := value.(types.Uint)
bs := bits.(types.Int)
if bs < types.IntZero {
return types.NewErr("math.bitShiftLeft() negative offset: %d", bs)
}
return v << bs
}
func bitShiftRightIntInt(value, bits ref.Val) ref.Val {
v := value.(types.Int)
bs := bits.(types.Int)
if bs < types.IntZero {
return types.NewErr("math.bitShiftRight() negative offset: %d", bs)
}
return types.Int(types.Uint(v) >> bs)
}
func bitShiftRightUintInt(value, bits ref.Val) ref.Val {
v := value.(types.Uint)
bs := bits.(types.Int)
if bs < types.IntZero {
return types.NewErr("math.bitShiftRight() negative offset: %d", bs)
}
return v >> bs
}
func minPair(first, second ref.Val) ref.Val {
cmp, ok := first.(traits.Comparer)
if !ok {
return types.MaybeNoSuchOverloadErr(first)
}
out := cmp.Compare(second)
if types.IsUnknownOrError(out) {
return maybeSuffixError(out, "math.@min")
}
if out == types.IntOne {
return second
}
return first
}
func minList(numList ref.Val) ref.Val {
l := numList.(traits.Lister)
size := l.Size().(types.Int)
if size == types.IntZero {
return types.NewErr("math.@min(list) argument must not be empty")
}
min := l.Get(types.IntZero)
for i := types.IntOne; i < size; i++ {
min = minPair(min, l.Get(i))
}
switch min.Type() {
case types.IntType, types.DoubleType, types.UintType, types.UnknownType:
return min
default:
return types.NewErr("no such overload: math.@min")
}
}
func maxPair(first, second ref.Val) ref.Val {
cmp, ok := first.(traits.Comparer)
if !ok {
return types.MaybeNoSuchOverloadErr(first)
}
out := cmp.Compare(second)
if types.IsUnknownOrError(out) {
return maybeSuffixError(out, "math.@max")
}
if out == types.IntNegOne {
return second
}
return first
}
func maxList(numList ref.Val) ref.Val {
l := numList.(traits.Lister)
size := l.Size().(types.Int)
if size == types.IntZero {
return types.NewErr("math.@max(list) argument must not be empty")
}
max := l.Get(types.IntZero)
for i := types.IntOne; i < size; i++ {
max = maxPair(max, l.Get(i))
}
switch max.Type() {
case types.IntType, types.DoubleType, types.UintType, types.UnknownType:
return max
default:
return types.NewErr("no such overload: math.@max")
}
}
func checkInvalidArgs(meh cel.MacroExprFactory, funcName string, args []ast.Expr) *cel.Error {
for _, arg := range args {
err := checkInvalidArgLiteral(funcName, arg)
if err != nil {
return meh.NewError(arg.ID(), err.Error())
}
}
return nil
}
func checkInvalidArgLiteral(funcName string, arg ast.Expr) error {
if !isNumericArgType(arg) {
return fmt.Errorf("%s simple literal arguments must be numeric", funcName)
}
return nil
}
func isNumericArgType(arg ast.Expr) bool {
switch arg.Kind() {
case ast.LiteralKind:
c := ref.Val(arg.AsLiteral())
switch c.(type) {
case types.Double, types.Int, types.Uint:
return true
default:
return false
}
case ast.ListKind, ast.MapKind, ast.StructKind:
return false
default:
return true
}
}
func isListLiteralWithNumericArgs(arg ast.Expr) bool {
switch arg.Kind() {
case ast.ListKind:
list := arg.AsList()
if list.Size() == 0 {
return false
}
for _, e := range list.Elements() {
if !isNumericArgType(e) {
return false
}
}
return true
}
return false
}
func maybeSuffixError(val ref.Val, suffix string) ref.Val {
if types.IsError(val) {
msg := val.(*types.Err).String()
if !strings.Contains(msg, suffix) {
return types.NewErr("%s: %s", msg, suffix)
}
}
return val
}
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package ext
import (
"errors"
"fmt"
"math"
"reflect"
"strings"
"time"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/reflect/protoreflect"
"github.com/google/cel-go/cel"
"github.com/google/cel-go/common/types"
"github.com/google/cel-go/common/types/pb"
"github.com/google/cel-go/common/types/ref"
"github.com/google/cel-go/common/types/traits"
structpb "google.golang.org/protobuf/types/known/structpb"
)
var (
nativeObjTraitMask = traits.FieldTesterType | traits.IndexerType
jsonValueType = reflect.TypeOf(&structpb.Value{})
jsonStructType = reflect.TypeOf(&structpb.Struct{})
)
// NativeTypes creates a type provider which uses reflect.Type and reflect.Value instances
// to produce type definitions that can be used within CEL.
//
// All struct types in Go are exposed to CEL via their simple package name and struct type name:
//
// ```go
// package identity
//
// type Account struct {
// ID int
// }
//
// ```
//
// The type `identity.Account` would be exported to CEL using the same qualified name, e.g.
// `identity.Account{ID: 1234}` would create a new `Account` instance with the `ID` field
// populated.
//
// Only exported fields are exposed via NativeTypes, and the type-mapping between Go and CEL
// is as follows:
//
// | Go type | CEL type |
// |-------------------------------------|-----------|
// | bool | bool |
// | []byte | bytes |
// | float32, float64 | double |
// | int, int8, int16, int32, int64 | int |
// | string | string |
// | uint, uint8, uint16, uint32, uint64 | uint |
// | time.Duration | duration |
// | time.Time | timestamp |
// | array, slice | list |
// | map | map |
//
// Please note, if you intend to configure support for proto messages in addition to native
// types, you will need to provide the protobuf types before the golang native types. The
// same advice holds if you are using custom type adapters and type providers. The native type
// provider composes over whichever type adapter and provider is configured in the cel.Env at
// the time that it is invoked.
//
// There is also the possibility to rename the fields of native structs by setting the `cel` tag
// for fields you want to override. In order to enable this feature, pass in the `ParseStructTags(true)`
// option. Here is an example to see it in action:
//
// ```go
// package identity
//
// type Account struct {
// ID int
// OwnerName string `cel:"owner"`
// }
//
// ```
//
// The `OwnerName` field is now accessible in CEL via `owner`, e.g. `identity.Account{owner: 'bob'}`.
// In case there are duplicated field names in the struct, an error will be returned.
func NativeTypes(args ...any) cel.EnvOption {
return func(env *cel.Env) (*cel.Env, error) {
nativeTypes := make([]any, 0, len(args))
tpOptions := nativeTypeOptions{
version: math.MaxUint32,
}
for _, v := range args {
switch v := v.(type) {
case NativeTypesOption:
err := v(&tpOptions)
if err != nil {
return nil, err
}
default:
nativeTypes = append(nativeTypes, v)
}
}
tp, err := newNativeTypeProvider(tpOptions, env.CELTypeAdapter(), env.CELTypeProvider(), nativeTypes...)
if err != nil {
return nil, err
}
env, err = cel.CustomTypeAdapter(tp)(env)
if err != nil {
return nil, err
}
return cel.CustomTypeProvider(tp)(env)
}
}
// NativeTypesOption is a functional interface for configuring handling of native types.
type NativeTypesOption func(*nativeTypeOptions) error
// NativeTypesVersion sets the native types version support for native extensions functions.
func NativeTypesVersion(version uint32) NativeTypesOption {
return func(opts *nativeTypeOptions) error {
opts.version = version
return nil
}
}
// NativeTypesFieldNameHandler is a handler for mapping a reflect.StructField to a CEL field name.
// This can be used to override the default Go struct field to CEL field name mapping.
type NativeTypesFieldNameHandler = func(field reflect.StructField) string
func fieldNameByTag(structTagToParse string) func(field reflect.StructField) string {
return func(field reflect.StructField) string {
tag, found := field.Tag.Lookup(structTagToParse)
if found {
splits := strings.Split(tag, ",")
if len(splits) > 0 {
// We make the assumption that the leftmost entry in the tag is the name.
// This seems to be true for most tags that have the concept of a name/key, such as:
// https://pkg.go.dev/encoding/xml#Marshal
// https://pkg.go.dev/encoding/json#Marshal
// https://pkg.go.dev/go.mongodb.org/mongo-driver/bson#hdr-Structs
// https://pkg.go.dev/gopkg.in/yaml.v2#Marshal
name := splits[0]
return name
}
}
return field.Name
}
}
type nativeTypeOptions struct {
// fieldNameHandler controls how CEL should perform struct field renames.
// This is most commonly used for switching to parsing based off the struct field tag,
// such as "cel" or "json".
fieldNameHandler NativeTypesFieldNameHandler
// version is the native types library version.
version uint32
}
// ParseStructTags configures if native types field names should be overridable by CEL struct tags.
// This is equivalent to ParseStructTag("cel")
func ParseStructTags(enabled bool) NativeTypesOption {
return func(ntp *nativeTypeOptions) error {
if enabled {
ntp.fieldNameHandler = fieldNameByTag("cel")
} else {
ntp.fieldNameHandler = nil
}
return nil
}
}
// ParseStructTag configures the struct tag to parse. The 0th item in the tag is used as the name of the CEL field.
// For example:
// If the tag to parse is "cel" and the struct field has tag cel:"foo", the CEL struct field will be "foo".
// If the tag to parse is "json" and the struct field has tag json:"foo,omitempty", the CEL struct field will be "foo".
func ParseStructTag(tag string) NativeTypesOption {
return func(ntp *nativeTypeOptions) error {
ntp.fieldNameHandler = fieldNameByTag(tag)
return nil
}
}
// ParseStructField configures how to parse Go struct fields. It can be used to customize struct field parsing.
func ParseStructField(handler NativeTypesFieldNameHandler) NativeTypesOption {
return func(ntp *nativeTypeOptions) error {
ntp.fieldNameHandler = handler
return nil
}
}
func newNativeTypeProvider(tpOptions nativeTypeOptions, adapter types.Adapter, provider types.Provider, refTypes ...any) (*nativeTypeProvider, error) {
nativeTypes := make(map[string]*nativeType, len(refTypes))
for _, refType := range refTypes {
switch rt := refType.(type) {
case reflect.Type:
result, err := newNativeTypes(tpOptions.fieldNameHandler, rt)
if err != nil {
return nil, err
}
for idx := range result {
nativeTypes[result[idx].TypeName()] = result[idx]
}
case reflect.Value:
result, err := newNativeTypes(tpOptions.fieldNameHandler, rt.Type())
if err != nil {
return nil, err
}
for idx := range result {
nativeTypes[result[idx].TypeName()] = result[idx]
}
default:
return nil, fmt.Errorf("unsupported native type: %v (%T) must be reflect.Type or reflect.Value", rt, rt)
}
}
return &nativeTypeProvider{
nativeTypes: nativeTypes,
baseAdapter: adapter,
baseProvider: provider,
options: tpOptions,
}, nil
}
type nativeTypeProvider struct {
nativeTypes map[string]*nativeType
baseAdapter types.Adapter
baseProvider types.Provider
options nativeTypeOptions
}
// EnumValue proxies to the types.Provider configured at the times the NativeTypes
// option was configured.
func (tp *nativeTypeProvider) EnumValue(enumName string) ref.Val {
return tp.baseProvider.EnumValue(enumName)
}
// FindIdent looks up natives type instances by qualified identifier, and if not found
// proxies to the composed types.Provider.
func (tp *nativeTypeProvider) FindIdent(typeName string) (ref.Val, bool) {
if t, found := tp.nativeTypes[typeName]; found {
return t, true
}
return tp.baseProvider.FindIdent(typeName)
}
// FindStructType looks up the CEL type definition by qualified identifier, and if not found
// proxies to the composed types.Provider.
func (tp *nativeTypeProvider) FindStructType(typeName string) (*types.Type, bool) {
if _, found := tp.nativeTypes[typeName]; found {
return types.NewTypeTypeWithParam(types.NewObjectType(typeName)), true
}
if celType, found := tp.baseProvider.FindStructType(typeName); found {
return celType, true
}
return tp.baseProvider.FindStructType(typeName)
}
func toFieldName(fieldNameHandler NativeTypesFieldNameHandler, f reflect.StructField) string {
if fieldNameHandler == nil {
return f.Name
}
return fieldNameHandler(f)
}
// FindStructFieldNames looks up the type definition first from the native types, then from
// the backing provider type set. If found, a set of field names corresponding to the type
// will be returned.
func (tp *nativeTypeProvider) FindStructFieldNames(typeName string) ([]string, bool) {
if t, found := tp.nativeTypes[typeName]; found {
fieldCount := t.refType.NumField()
fields := make([]string, fieldCount)
for i := 0; i < fieldCount; i++ {
fields[i] = toFieldName(tp.options.fieldNameHandler, t.refType.Field(i))
}
return fields, true
}
if celTypeFields, found := tp.baseProvider.FindStructFieldNames(typeName); found {
return celTypeFields, true
}
return tp.baseProvider.FindStructFieldNames(typeName)
}
// FindStructFieldType looks up a native type's field definition, and if the type name is not a native
// type then proxies to the composed types.Provider
func (tp *nativeTypeProvider) FindStructFieldType(typeName, fieldName string) (*types.FieldType, bool) {
t, found := tp.nativeTypes[typeName]
if !found {
return tp.baseProvider.FindStructFieldType(typeName, fieldName)
}
refField, isDefined := t.hasField(fieldName)
if !found || !isDefined {
return nil, false
}
celType, ok := convertToCelType(refField.Type)
if !ok {
return nil, false
}
return &types.FieldType{
Type: celType,
IsSet: func(obj any) bool {
refVal := reflect.Indirect(reflect.ValueOf(obj))
refField := refVal.FieldByName(refField.Name)
return !refField.IsZero()
},
GetFrom: func(obj any) (any, error) {
refVal := reflect.Indirect(reflect.ValueOf(obj))
refField := refVal.FieldByName(refField.Name)
return getFieldValue(refField), nil
},
}, true
}
// NewValue implements the ref.TypeProvider interface method.
func (tp *nativeTypeProvider) NewValue(typeName string, fields map[string]ref.Val) ref.Val {
t, found := tp.nativeTypes[typeName]
if !found {
return tp.baseProvider.NewValue(typeName, fields)
}
refPtr := reflect.New(t.refType)
refVal := refPtr.Elem()
for fieldName, val := range fields {
refFieldDef, isDefined := t.hasField(fieldName)
if !isDefined {
return types.NewErr("no such field: %s", fieldName)
}
fieldVal, err := val.ConvertToNative(refFieldDef.Type)
if err != nil {
return types.NewErrFromString(err.Error())
}
refField := refVal.FieldByIndex(refFieldDef.Index)
refFieldVal := reflect.ValueOf(fieldVal)
refField.Set(refFieldVal)
}
return tp.NativeToValue(refPtr.Interface())
}
// NewValue adapts native values to CEL values and will proxy to the composed type adapter
// for non-native types.
func (tp *nativeTypeProvider) NativeToValue(val any) ref.Val {
if val == nil {
return types.NullValue
}
if v, ok := val.(ref.Val); ok {
return v
}
rawVal := reflect.ValueOf(val)
refVal := rawVal
if refVal.Kind() == reflect.Ptr {
refVal = reflect.Indirect(refVal)
}
// This isn't quite right if you're also supporting proto,
// but maybe an acceptable limitation.
switch refVal.Kind() {
case reflect.Array, reflect.Slice:
switch val := val.(type) {
case []byte:
return tp.baseAdapter.NativeToValue(val)
default:
if refVal.Type().Elem() == reflect.TypeOf(byte(0)) {
return tp.baseAdapter.NativeToValue(val)
}
return types.NewDynamicList(tp, val)
}
case reflect.Map:
return types.NewDynamicMap(tp, val)
case reflect.Struct:
switch val := val.(type) {
case proto.Message, *pb.Map, protoreflect.List, protoreflect.Message, protoreflect.Value,
time.Time:
return tp.baseAdapter.NativeToValue(val)
default:
return tp.newNativeObject(val, rawVal)
}
default:
return tp.baseAdapter.NativeToValue(val)
}
}
func convertToCelType(refType reflect.Type) (*cel.Type, bool) {
switch refType.Kind() {
case reflect.Bool:
return cel.BoolType, true
case reflect.Float32, reflect.Float64:
return cel.DoubleType, true
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
if refType == durationType {
return cel.DurationType, true
}
return cel.IntType, true
case reflect.String:
return cel.StringType, true
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
return cel.UintType, true
case reflect.Array, reflect.Slice:
refElem := refType.Elem()
if refElem == reflect.TypeOf(byte(0)) {
return cel.BytesType, true
}
elemType, ok := convertToCelType(refElem)
if !ok {
return nil, false
}
return cel.ListType(elemType), true
case reflect.Map:
keyType, ok := convertToCelType(refType.Key())
if !ok {
return nil, false
}
// Ensure the key type is a int, bool, uint, string
elemType, ok := convertToCelType(refType.Elem())
if !ok {
return nil, false
}
return cel.MapType(keyType, elemType), true
case reflect.Struct:
if refType == timestampType {
return cel.TimestampType, true
}
return cel.ObjectType(
fmt.Sprintf("%s.%s", simplePkgAlias(refType.PkgPath()), refType.Name()),
), true
case reflect.Pointer:
if refType.Implements(pbMsgInterfaceType) {
pbMsg := reflect.New(refType.Elem()).Interface().(protoreflect.ProtoMessage)
return cel.ObjectType(string(pbMsg.ProtoReflect().Descriptor().FullName())), true
}
return convertToCelType(refType.Elem())
}
return nil, false
}
func (tp *nativeTypeProvider) newNativeObject(val any, refValue reflect.Value) ref.Val {
valType, err := newNativeType(tp.options.fieldNameHandler, refValue.Type())
if err != nil {
return types.NewErrFromString(err.Error())
}
return &nativeObj{
Adapter: tp,
val: val,
valType: valType,
refValue: refValue,
}
}
type nativeObj struct {
types.Adapter
val any
valType *nativeType
refValue reflect.Value
}
// ConvertToNative implements the ref.Val interface method.
//
// CEL does not have a notion of pointers, so whether a field is a pointer or value
// is handled as part of this conversion step.
func (o *nativeObj) ConvertToNative(typeDesc reflect.Type) (any, error) {
if o.refValue.Type() == typeDesc {
return o.val, nil
}
if o.refValue.Kind() == reflect.Pointer && o.refValue.Type().Elem() == typeDesc {
return o.refValue.Elem().Interface(), nil
}
if typeDesc.Kind() == reflect.Pointer && o.refValue.Type() == typeDesc.Elem() {
ptr := reflect.New(typeDesc.Elem())
ptr.Elem().Set(o.refValue)
return ptr.Interface(), nil
}
switch typeDesc {
case jsonValueType:
jsonStruct, err := o.ConvertToNative(jsonStructType)
if err != nil {
return nil, err
}
return structpb.NewStructValue(jsonStruct.(*structpb.Struct)), nil
case jsonStructType:
refVal := reflect.Indirect(o.refValue)
refType := refVal.Type()
fields := make(map[string]*structpb.Value, refVal.NumField())
for i := 0; i < refVal.NumField(); i++ {
fieldType := refType.Field(i)
fieldValue := refVal.Field(i)
if !fieldValue.IsValid() || fieldValue.IsZero() {
continue
}
fieldName := toFieldName(o.valType.fieldNameHandler, fieldType)
fieldCELVal := o.NativeToValue(fieldValue.Interface())
fieldJSONVal, err := fieldCELVal.ConvertToNative(jsonValueType)
if err != nil {
return nil, err
}
fields[fieldName] = fieldJSONVal.(*structpb.Value)
}
return &structpb.Struct{Fields: fields}, nil
}
return nil, fmt.Errorf("type conversion error from '%v' to '%v'", o.Type(), typeDesc)
}
// ConvertToType implements the ref.Val interface method.
func (o *nativeObj) ConvertToType(typeVal ref.Type) ref.Val {
switch typeVal {
case types.TypeType:
return o.valType
default:
if typeVal.TypeName() == o.valType.typeName {
return o
}
}
return types.NewErr("type conversion error from '%s' to '%s'", o.Type(), typeVal)
}
// Equal implements the ref.Val interface method.
//
// Note, that in Golang a pointer to a value is not equal to the value it contains.
// In CEL pointers and values to which they point are equal.
func (o *nativeObj) Equal(other ref.Val) ref.Val {
otherNtv, ok := other.(*nativeObj)
if !ok {
return types.False
}
val := o.val
otherVal := otherNtv.val
refVal := o.refValue
otherRefVal := otherNtv.refValue
if refVal.Kind() != otherRefVal.Kind() {
if refVal.Kind() == reflect.Pointer {
val = refVal.Elem().Interface()
} else if otherRefVal.Kind() == reflect.Pointer {
otherVal = otherRefVal.Elem().Interface()
}
}
return types.Bool(reflect.DeepEqual(val, otherVal))
}
// IsZeroValue indicates whether the contained Golang value is a zero value.
//
// Golang largely follows proto3 semantics for zero values.
func (o *nativeObj) IsZeroValue() bool {
return reflect.Indirect(o.refValue).IsZero()
}
// IsSet tests whether a field which is defined is set to a non-default value.
func (o *nativeObj) IsSet(field ref.Val) ref.Val {
refField, refErr := o.getReflectedField(field)
if refErr != nil {
return refErr
}
return types.Bool(!refField.IsZero())
}
// Get returns the value fo a field name.
func (o *nativeObj) Get(field ref.Val) ref.Val {
refField, refErr := o.getReflectedField(field)
if refErr != nil {
return refErr
}
return adaptFieldValue(o, refField)
}
func (o *nativeObj) getReflectedField(field ref.Val) (reflect.Value, ref.Val) {
fieldName, ok := field.(types.String)
if !ok {
return reflect.Value{}, types.MaybeNoSuchOverloadErr(field)
}
fieldNameStr := string(fieldName)
refField, isDefined := o.valType.hasField(fieldNameStr)
if !isDefined {
return reflect.Value{}, types.NewErr("no such field: %s", fieldName)
}
refVal := reflect.Indirect(o.refValue)
return refVal.FieldByIndex(refField.Index), nil
}
// Type implements the ref.Val interface method.
func (o *nativeObj) Type() ref.Type {
return o.valType
}
// Value implements the ref.Val interface method.
func (o *nativeObj) Value() any {
return o.val
}
func newNativeTypes(fieldNameHandler NativeTypesFieldNameHandler, rawType reflect.Type) ([]*nativeType, error) {
nt, err := newNativeType(fieldNameHandler, rawType)
if err != nil {
return nil, err
}
result := []*nativeType{nt}
alreadySeen := make(map[string]struct{})
var iterateStructMembers func(reflect.Type)
iterateStructMembers = func(t reflect.Type) {
if k := t.Kind(); k == reflect.Pointer || k == reflect.Slice || k == reflect.Array || k == reflect.Map {
t = t.Elem()
}
if t.Kind() != reflect.Struct {
return
}
if _, seen := alreadySeen[t.String()]; seen {
return
}
alreadySeen[t.String()] = struct{}{}
nt, ntErr := newNativeType(fieldNameHandler, t)
if ntErr != nil {
err = ntErr
return
}
result = append(result, nt)
for idx := 0; idx < t.NumField(); idx++ {
iterateStructMembers(t.Field(idx).Type)
}
}
iterateStructMembers(rawType)
return result, err
}
var (
errDuplicatedFieldName = errors.New("field name already exists in struct")
)
func newNativeType(fieldNameHandler NativeTypesFieldNameHandler, rawType reflect.Type) (*nativeType, error) {
refType := rawType
if refType.Kind() == reflect.Pointer {
refType = refType.Elem()
}
if !isValidObjectType(refType) {
return nil, fmt.Errorf("unsupported reflect.Type %v, must be reflect.Struct", rawType)
}
// Since naming collisions can only happen with struct tag parsing, we only check for them if it is enabled.
if fieldNameHandler != nil {
fieldNames := make(map[string]struct{})
for idx := 0; idx < refType.NumField(); idx++ {
field := refType.Field(idx)
fieldName := toFieldName(fieldNameHandler, field)
if _, found := fieldNames[fieldName]; found {
return nil, fmt.Errorf("invalid field name `%s` in struct `%s`: %w", fieldName, refType.Name(), errDuplicatedFieldName)
} else {
fieldNames[fieldName] = struct{}{}
}
}
}
return &nativeType{
typeName: fmt.Sprintf("%s.%s", simplePkgAlias(refType.PkgPath()), refType.Name()),
refType: refType,
fieldNameHandler: fieldNameHandler,
}, nil
}
type nativeType struct {
typeName string
refType reflect.Type
fieldNameHandler NativeTypesFieldNameHandler
}
// ConvertToNative implements ref.Val.ConvertToNative.
func (t *nativeType) ConvertToNative(typeDesc reflect.Type) (any, error) {
return nil, fmt.Errorf("type conversion error for type to '%v'", typeDesc)
}
// ConvertToType implements ref.Val.ConvertToType.
func (t *nativeType) ConvertToType(typeVal ref.Type) ref.Val {
switch typeVal {
case types.TypeType:
return types.TypeType
}
return types.NewErr("type conversion error from '%s' to '%s'", types.TypeType, typeVal)
}
// Equal returns true of both type names are equal to each other.
func (t *nativeType) Equal(other ref.Val) ref.Val {
otherType, ok := other.(ref.Type)
return types.Bool(ok && t.TypeName() == otherType.TypeName())
}
// HasTrait implements the ref.Type interface method.
func (t *nativeType) HasTrait(trait int) bool {
return nativeObjTraitMask&trait == trait
}
// String implements the strings.Stringer interface method.
func (t *nativeType) String() string {
return t.typeName
}
// Type implements the ref.Val interface method.
func (t *nativeType) Type() ref.Type {
return types.TypeType
}
// TypeName implements the ref.Type interface method.
func (t *nativeType) TypeName() string {
return t.typeName
}
// Value implements the ref.Val interface method.
func (t *nativeType) Value() any {
return t.typeName
}
// fieldByName returns the corresponding reflect.StructField for the give name either by matching
// field tag or field name.
func (t *nativeType) fieldByName(fieldName string) (reflect.StructField, bool) {
if t.fieldNameHandler == nil {
return t.refType.FieldByName(fieldName)
}
for i := 0; i < t.refType.NumField(); i++ {
f := t.refType.Field(i)
if toFieldName(t.fieldNameHandler, f) == fieldName {
return f, true
}
}
return reflect.StructField{}, false
}
// hasField returns whether a field name has a corresponding Golang reflect.StructField
func (t *nativeType) hasField(fieldName string) (reflect.StructField, bool) {
f, found := t.fieldByName(fieldName)
if !found || !f.IsExported() || !isSupportedType(f.Type) {
return reflect.StructField{}, false
}
return f, true
}
func adaptFieldValue(adapter types.Adapter, refField reflect.Value) ref.Val {
return adapter.NativeToValue(getFieldValue(refField))
}
func getFieldValue(refField reflect.Value) any {
if refField.IsZero() {
switch refField.Kind() {
case reflect.Struct:
if refField.Type() == timestampType {
return time.Unix(0, 0)
}
case reflect.Pointer:
return reflect.New(refField.Type().Elem()).Interface()
}
}
return refField.Interface()
}
func simplePkgAlias(pkgPath string) string {
paths := strings.Split(pkgPath, "/")
if len(paths) == 0 {
return ""
}
return paths[len(paths)-1]
}
func isValidObjectType(refType reflect.Type) bool {
return refType.Kind() == reflect.Struct
}
func isSupportedType(refType reflect.Type) bool {
switch refType.Kind() {
case reflect.Chan, reflect.Complex64, reflect.Complex128, reflect.Func, reflect.UnsafePointer, reflect.Uintptr:
return false
case reflect.Array, reflect.Slice:
return isSupportedType(refType.Elem())
case reflect.Map:
return isSupportedType(refType.Key()) && isSupportedType(refType.Elem())
}
return true
}
var (
pbMsgInterfaceType = reflect.TypeOf((*protoreflect.ProtoMessage)(nil)).Elem()
timestampType = reflect.TypeOf(time.Now())
durationType = reflect.TypeOf(time.Nanosecond)
)
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package ext
import (
"math"
"github.com/google/cel-go/cel"
"github.com/google/cel-go/common/ast"
)
// Protos returns a cel.EnvOption to configure extended macros and functions for
// proto manipulation.
//
// Note, all macros use the 'proto' namespace; however, at the time of macro
// expansion the namespace looks just like any other identifier. If you are
// currently using a variable named 'proto', the macro will likely work just as
// intended; however, there is some chance for collision.
//
// # Protos.GetExt
//
// Macro which generates a select expression that retrieves an extension field
// from the input proto2 syntax message. If the field is not set, the default
// value forthe extension field is returned according to safe-traversal semantics.
//
// proto.getExt(<msg>, <fully.qualified.extension.name>) -> <field-type>
//
// Examples:
//
// proto.getExt(msg, google.expr.proto2.test.int32_ext) // returns int value
//
// # Protos.HasExt
//
// Macro which generates a test-only select expression that determines whether
// an extension field is set on a proto2 syntax message.
//
// proto.hasExt(<msg>, <fully.qualified.extension.name>) -> <bool>
//
// Examples:
//
// proto.hasExt(msg, google.expr.proto2.test.int32_ext) // returns true || false
func Protos(options ...ProtosOption) cel.EnvOption {
l := &protoLib{version: math.MaxUint32}
for _, o := range options {
l = o(l)
}
return cel.Lib(l)
}
// ProtosOption declares a functional operator for configuring protobuf utilities.
type ProtosOption func(*protoLib) *protoLib
// ProtosVersion sets the library version for extensions for protobuf utilities.
func ProtosVersion(version uint32) ProtosOption {
return func(lib *protoLib) *protoLib {
lib.version = version
return lib
}
}
var (
protoNamespace = "proto"
hasExtension = "hasExt"
getExtension = "getExt"
)
type protoLib struct {
version uint32
}
// LibraryName implements the SingletonLibrary interface method.
func (protoLib) LibraryName() string {
return "cel.lib.ext.protos"
}
// CompileOptions implements the Library interface method.
func (protoLib) CompileOptions() []cel.EnvOption {
return []cel.EnvOption{
cel.Macros(
// proto.getExt(msg, select_expression)
cel.ReceiverMacro(getExtension, 2, getProtoExt),
// proto.hasExt(msg, select_expression)
cel.ReceiverMacro(hasExtension, 2, hasProtoExt),
),
}
}
// ProgramOptions implements the Library interface method.
func (protoLib) ProgramOptions() []cel.ProgramOption {
return []cel.ProgramOption{}
}
// hasProtoExt generates a test-only select expression for a fully-qualified extension name on a protobuf message.
func hasProtoExt(mef cel.MacroExprFactory, target ast.Expr, args []ast.Expr) (ast.Expr, *cel.Error) {
if !macroTargetMatchesNamespace(protoNamespace, target) {
return nil, nil
}
extensionField, err := getExtFieldName(mef, args[1])
if err != nil {
return nil, err
}
return mef.NewPresenceTest(args[0], extensionField), nil
}
// getProtoExt generates a select expression for a fully-qualified extension name on a protobuf message.
func getProtoExt(mef cel.MacroExprFactory, target ast.Expr, args []ast.Expr) (ast.Expr, *cel.Error) {
if !macroTargetMatchesNamespace(protoNamespace, target) {
return nil, nil
}
extFieldName, err := getExtFieldName(mef, args[1])
if err != nil {
return nil, err
}
return mef.NewSelect(args[0], extFieldName), nil
}
func getExtFieldName(mef cel.MacroExprFactory, expr ast.Expr) (string, *cel.Error) {
isValid := false
extensionField := ""
switch expr.Kind() {
case ast.SelectKind:
extensionField, isValid = validateIdentifier(expr)
}
if !isValid {
return "", mef.NewError(expr.ID(), "invalid extension field")
}
return extensionField, nil
}
func validateIdentifier(expr ast.Expr) (string, bool) {
switch expr.Kind() {
case ast.IdentKind:
return expr.AsIdent(), true
case ast.SelectKind:
sel := expr.AsSelect()
if sel.IsTestOnly() {
return "", false
}
opStr, isIdent := validateIdentifier(sel.Operand())
if !isIdent {
return "", false
}
return opStr + "." + sel.FieldName(), true
default:
return "", false
}
}
// Copyright 2025 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package ext
import (
"errors"
"fmt"
"math"
"regexp"
"strconv"
"strings"
"github.com/google/cel-go/cel"
"github.com/google/cel-go/common/types"
"github.com/google/cel-go/common/types/ref"
)
const (
regexReplace = "regex.replace"
regexExtract = "regex.extract"
regexExtractAll = "regex.extractAll"
)
// Regex returns a cel.EnvOption to configure extended functions for regular
// expression operations.
//
// Note: all functions use the 'regex' namespace. If you are
// currently using a variable named 'regex', the functions will likely work as
// intended, however there is some chance for collision.
//
// This library depends on the CEL optional type. Please ensure that the
// cel.OptionalTypes() is enabled when using regex extensions.
//
// # Replace
//
// The `regex.replace` function replaces all non-overlapping substring of a regex
// pattern in the target string with a replacement string. Optionally, you can
// limit the number of replacements by providing a count argument. When the count
// is a negative number, the function acts as replace all. Only numeric (\N)
// capture group references are supported in the replacement string, with
// validation for correctness. Backslashed-escaped digits (\1 to \9) within the
// replacement argument can be used to insert text matching the corresponding
// parenthesized group in the regexp pattern. An error will be thrown for invalid
// regex or replace string.
//
// regex.replace(target: string, pattern: string, replacement: string) -> string
// regex.replace(target: string, pattern: string, replacement: string, count: int) -> string
//
// Examples:
//
// regex.replace('hello world hello', 'hello', 'hi') == 'hi world hi'
// regex.replace('banana', 'a', 'x', 0) == 'banana'
// regex.replace('banana', 'a', 'x', 1) == 'bxnana'
// regex.replace('banana', 'a', 'x', 2) == 'bxnxna'
// regex.replace('banana', 'a', 'x', -12) == 'bxnxnx'
// regex.replace('foo bar', '(fo)o (ba)r', r'\2 \1') == 'ba fo'
// regex.replace('test', '(.)', r'\2') \\ Runtime Error invalid replace string
// regex.replace('foo bar', '(', '$2 $1') \\ Runtime Error invalid regex string
// regex.replace('id=123', r'id=(?P<value>\d+)', r'value: \values') \\ Runtime Error invalid replace string
//
// # Extract
//
// The `regex.extract` function returns the first match of a regex pattern in a
// string. If no match is found, it returns an optional none value. An error will
// be thrown for invalid regex or for multiple capture groups.
//
// regex.extract(target: string, pattern: string) -> optional<string>
//
// Examples:
//
// regex.extract('hello world', 'hello(.*)') == optional.of(' world')
// regex.extract('item-A, item-B', 'item-(\\w+)') == optional.of('A')
// regex.extract('HELLO', 'hello') == optional.empty()
// regex.extract('testuser@testdomain', '(.*)@([^.]*)') // Runtime Error multiple capture group
//
// # Extract All
//
// The `regex.extractAll` function returns a list of all matches of a regex
// pattern in a target string. If no matches are found, it returns an empty list. An error will
// be thrown for invalid regex or for multiple capture groups.
//
// regex.extractAll(target: string, pattern: string) -> list<string>
//
// Examples:
//
// regex.extractAll('id:123, id:456', 'id:\\d+') == ['id:123', 'id:456']
// regex.extractAll('id:123, id:456', 'assa') == []
// regex.extractAll('testuser@testdomain', '(.*)@([^.]*)') // Runtime Error multiple capture group
func Regex(options ...RegexOptions) cel.EnvOption {
s := ®exLib{
version: math.MaxUint32,
}
for _, o := range options {
s = o(s)
}
return cel.Lib(s)
}
// RegexOptions declares a functional operator for configuring regex extension.
type RegexOptions func(*regexLib) *regexLib
// RegexVersion configures the version of the Regex library definitions to use. See [Regex] for supported values.
func RegexVersion(version uint32) RegexOptions {
return func(lib *regexLib) *regexLib {
lib.version = version
return lib
}
}
type regexLib struct {
version uint32
}
// LibraryName implements that SingletonLibrary interface method.
func (r *regexLib) LibraryName() string {
return "cel.lib.ext.regex"
}
// CompileOptions implements the cel.Library interface method.
func (r *regexLib) CompileOptions() []cel.EnvOption {
optionalTypesEnabled := func(env *cel.Env) (*cel.Env, error) {
if !env.HasLibrary("cel.lib.optional") {
return nil, errors.New("regex library requires the optional library")
}
return env, nil
}
opts := []cel.EnvOption{
cel.Function(regexExtract,
cel.Overload("regex_extract_string_string", []*cel.Type{cel.StringType, cel.StringType}, cel.OptionalType(cel.StringType),
cel.BinaryBinding(extract))),
cel.Function(regexExtractAll,
cel.Overload("regex_extractAll_string_string", []*cel.Type{cel.StringType, cel.StringType}, cel.ListType(cel.StringType),
cel.BinaryBinding(extractAll))),
cel.Function(regexReplace,
cel.Overload("regex_replace_string_string_string", []*cel.Type{cel.StringType, cel.StringType, cel.StringType}, cel.StringType,
cel.FunctionBinding(regReplace)),
cel.Overload("regex_replace_string_string_string_int", []*cel.Type{cel.StringType, cel.StringType, cel.StringType, cel.IntType}, cel.StringType,
cel.FunctionBinding((regReplaceN))),
),
cel.EnvOption(optionalTypesEnabled),
}
return opts
}
// ProgramOptions implements the cel.Library interface method
func (r *regexLib) ProgramOptions() []cel.ProgramOption {
return []cel.ProgramOption{}
}
func compileRegex(regexStr string) (*regexp.Regexp, error) {
re, err := regexp.Compile(regexStr)
if err != nil {
return nil, fmt.Errorf("given regex is invalid: %w", err)
}
return re, nil
}
func regReplace(args ...ref.Val) ref.Val {
target := args[0].(types.String)
regexStr := args[1].(types.String)
replaceStr := args[2].(types.String)
return regReplaceN(target, regexStr, replaceStr, types.Int(-1))
}
func regReplaceN(args ...ref.Val) ref.Val {
target := string(args[0].(types.String))
regexStr := string(args[1].(types.String))
replaceStr := string(args[2].(types.String))
replaceCount := int64(args[3].(types.Int))
if replaceCount == 0 {
return types.String(target)
}
if replaceCount > math.MaxInt32 {
return types.NewErr("integer overflow")
}
// If replaceCount is negative, just do a replaceAll.
if replaceCount < 0 {
replaceCount = -1
}
re, err := regexp.Compile(regexStr)
if err != nil {
return types.WrapErr(err)
}
var resultBuilder strings.Builder
var lastIndex int
counter := int64(0)
matches := re.FindAllStringSubmatchIndex(target, -1)
for _, match := range matches {
if replaceCount != -1 && counter >= replaceCount {
break
}
processedReplacement, err := replaceStrValidator(target, re, match, replaceStr)
if err != nil {
return types.WrapErr(err)
}
resultBuilder.WriteString(target[lastIndex:match[0]])
resultBuilder.WriteString(processedReplacement)
lastIndex = match[1]
counter++
}
resultBuilder.WriteString(target[lastIndex:])
return types.String(resultBuilder.String())
}
func replaceStrValidator(target string, re *regexp.Regexp, match []int, replacement string) (string, error) {
groupCount := re.NumSubexp()
var sb strings.Builder
runes := []rune(replacement)
for i := 0; i < len(runes); i++ {
c := runes[i]
if c != '\\' {
sb.WriteRune(c)
continue
}
if i+1 >= len(runes) {
return "", fmt.Errorf("invalid replacement string: '%s' \\ not allowed at end", replacement)
}
i++
nextChar := runes[i]
if nextChar == '\\' {
sb.WriteRune('\\')
continue
}
groupNum, err := strconv.Atoi(string(nextChar))
if err != nil {
return "", fmt.Errorf("invalid replacement string: '%s' \\ must be followed by a digit or \\", replacement)
}
if groupNum > groupCount {
return "", fmt.Errorf("replacement string references group %d but regex has only %d group(s)", groupNum, groupCount)
}
if match[2*groupNum] != -1 {
sb.WriteString(target[match[2*groupNum]:match[2*groupNum+1]])
}
}
return sb.String(), nil
}
func extract(target, regexStr ref.Val) ref.Val {
t := string(target.(types.String))
r := string(regexStr.(types.String))
re, err := compileRegex(r)
if err != nil {
return types.WrapErr(err)
}
if len(re.SubexpNames())-1 > 1 {
return types.WrapErr(fmt.Errorf("regular expression has more than one capturing group: %q", r))
}
matches := re.FindStringSubmatch(t)
if len(matches) == 0 {
return types.OptionalNone
}
// If there is a capturing group, return the first match; otherwise, return the whole match.
if len(matches) > 1 {
capturedGroup := matches[1]
// If optional group is empty, return OptionalNone.
if capturedGroup == "" {
return types.OptionalNone
}
return types.OptionalOf(types.String(capturedGroup))
}
return types.OptionalOf(types.String(matches[0]))
}
func extractAll(target, regexStr ref.Val) ref.Val {
t := string(target.(types.String))
r := string(regexStr.(types.String))
re, err := compileRegex(r)
if err != nil {
return types.WrapErr(err)
}
groupCount := len(re.SubexpNames()) - 1
if groupCount > 1 {
return types.WrapErr(fmt.Errorf("regular expression has more than one capturing group: %q", r))
}
matches := re.FindAllStringSubmatch(t, -1)
result := make([]string, 0, len(matches))
if len(matches) == 0 {
return types.NewStringList(types.DefaultTypeAdapter, result)
}
if groupCount != 1 {
for _, match := range matches {
result = append(result, match[0])
}
return types.NewStringList(types.DefaultTypeAdapter, result)
}
for _, match := range matches {
if match[1] != "" {
result = append(result, match[1])
}
}
return types.NewStringList(types.DefaultTypeAdapter, result)
}
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package ext
import (
"math"
"github.com/google/cel-go/cel"
"github.com/google/cel-go/checker"
"github.com/google/cel-go/common/ast"
"github.com/google/cel-go/common/operators"
"github.com/google/cel-go/common/types"
"github.com/google/cel-go/common/types/ref"
"github.com/google/cel-go/common/types/traits"
"github.com/google/cel-go/interpreter"
)
// Sets returns a cel.EnvOption to configure namespaced set relationship
// functions.
//
// There is no set type within CEL, and while one may be introduced in the
// future, there are cases where a `list` type is known to behave like a set.
// For such cases, this library provides some basic functionality for
// determining set containment, equivalence, and intersection.
//
// # Sets.Contains
//
// Returns whether the first list argument contains all elements in the second
// list argument. The list may contain elements of any type and standard CEL
// equality is used to determine whether a value exists in both lists. If the
// second list is empty, the result will always return true.
//
// sets.contains(list(T), list(T)) -> bool
//
// Examples:
//
// sets.contains([], []) // true
// sets.contains([], [1]) // false
// sets.contains([1, 2, 3, 4], [2, 3]) // true
// sets.contains([1, 2.0, 3u], [1.0, 2u, 3]) // true
//
// # Sets.Equivalent
//
// Returns whether the first and second list are set equivalent. Lists are set
// equivalent if for every item in the first list, there is an element in the
// second which is equal. The lists may not be of the same size as they do not
// guarantee the elements within them are unique, so size does not factor into
// the computation.
//
// Examples:
//
// sets.equivalent([], []) // true
// sets.equivalent([1], [1, 1]) // true
// sets.equivalent([1], [1u, 1.0]) // true
// sets.equivalent([1, 2, 3], [3u, 2.0, 1]) // true
//
// # Sets.Intersects
//
// Returns whether the first list has at least one element whose value is equal
// to an element in the second list. If either list is empty, the result will
// be false.
//
// Examples:
//
// sets.intersects([1], []) // false
// sets.intersects([1], [1, 2]) // true
// sets.intersects([[1], [2, 3]], [[1, 2], [2, 3.0]]) // true
func Sets(options ...SetsOption) cel.EnvOption {
l := &setsLib{}
for _, o := range options {
l = o(l)
}
return cel.Lib(l)
}
// SetsOption declares a functional operator for configuring set extensions.
type SetsOption func(*setsLib) *setsLib
// SetsVersion sets the library version for set extensions.
func SetsVersion(version uint32) SetsOption {
return func(lib *setsLib) *setsLib {
lib.version = version
return lib
}
}
type setsLib struct {
version uint32
}
// LibraryName implements the SingletonLibrary interface method.
func (setsLib) LibraryName() string {
return "cel.lib.ext.sets"
}
// CompileOptions implements the Library interface method.
func (setsLib) CompileOptions() []cel.EnvOption {
listType := cel.ListType(cel.TypeParamType("T"))
return []cel.EnvOption{
cel.Function("sets.contains",
cel.Overload("list_sets_contains_list", []*cel.Type{listType, listType}, cel.BoolType,
cel.BinaryBinding(setsContains))),
cel.Function("sets.equivalent",
cel.Overload("list_sets_equivalent_list", []*cel.Type{listType, listType}, cel.BoolType,
cel.BinaryBinding(setsEquivalent))),
cel.Function("sets.intersects",
cel.Overload("list_sets_intersects_list", []*cel.Type{listType, listType}, cel.BoolType,
cel.BinaryBinding(setsIntersects))),
cel.CostEstimatorOptions(
checker.OverloadCostEstimate("list_sets_contains_list", estimateSetsCost(1)),
checker.OverloadCostEstimate("list_sets_intersects_list", estimateSetsCost(1)),
// equivalence requires potentially two m*n comparisons to ensure each list is contained by the other
checker.OverloadCostEstimate("list_sets_equivalent_list", estimateSetsCost(2)),
),
}
}
// ProgramOptions implements the Library interface method.
func (setsLib) ProgramOptions() []cel.ProgramOption {
return []cel.ProgramOption{
cel.CostTrackerOptions(
interpreter.OverloadCostTracker("list_sets_contains_list", trackSetsCost(1)),
interpreter.OverloadCostTracker("list_sets_intersects_list", trackSetsCost(1)),
interpreter.OverloadCostTracker("list_sets_equivalent_list", trackSetsCost(2)),
),
}
}
// NewSetMembershipOptimizer rewrites set membership tests using the `in` operator against a list
// of constant values of enum, int, uint, string, or boolean type into a set membership test against
// a map where the map keys are the elements of the list.
func NewSetMembershipOptimizer() (cel.ASTOptimizer, error) {
return setsLib{}, nil
}
func (setsLib) Optimize(ctx *cel.OptimizerContext, a *ast.AST) *ast.AST {
root := ast.NavigateAST(a)
matches := ast.MatchDescendants(root, matchInConstantList(a))
for _, match := range matches {
call := match.AsCall()
listArg := call.Args()[1]
entries := make([]ast.EntryExpr, len(listArg.AsList().Elements()))
for i, elem := range listArg.AsList().Elements() {
var entry ast.EntryExpr
if r, found := a.ReferenceMap()[elem.ID()]; found && r.Value != nil {
entry = ctx.NewMapEntry(ctx.NewLiteral(r.Value), ctx.NewLiteral(types.True), false)
} else {
entry = ctx.NewMapEntry(elem, ctx.NewLiteral(types.True), false)
}
entries[i] = entry
}
mapArg := ctx.NewMap(entries)
ctx.UpdateExpr(listArg, mapArg)
}
return a
}
func matchInConstantList(a *ast.AST) ast.ExprMatcher {
return func(e ast.NavigableExpr) bool {
if e.Kind() != ast.CallKind {
return false
}
call := e.AsCall()
if call.FunctionName() != operators.In {
return false
}
aggregateVal := call.Args()[1]
if aggregateVal.Kind() != ast.ListKind {
return false
}
listVal := aggregateVal.AsList()
for _, elem := range listVal.Elements() {
if r, found := a.ReferenceMap()[elem.ID()]; found {
if r.Value != nil {
continue
}
}
if elem.Kind() != ast.LiteralKind {
return false
}
lit := elem.AsLiteral()
if !(lit.Type() == cel.StringType || lit.Type() == cel.IntType ||
lit.Type() == cel.UintType || lit.Type() == cel.BoolType) {
return false
}
}
return true
}
}
func setsIntersects(listA, listB ref.Val) ref.Val {
lA := listA.(traits.Lister)
lB := listB.(traits.Lister)
it := lA.Iterator()
for it.HasNext() == types.True {
exists := lB.Contains(it.Next())
if exists == types.True {
return types.True
}
}
return types.False
}
func setsContains(list, sublist ref.Val) ref.Val {
l := list.(traits.Lister)
sub := sublist.(traits.Lister)
it := sub.Iterator()
for it.HasNext() == types.True {
exists := l.Contains(it.Next())
if exists != types.True {
return exists
}
}
return types.True
}
func setsEquivalent(listA, listB ref.Val) ref.Val {
aContainsB := setsContains(listA, listB)
if aContainsB != types.True {
return aContainsB
}
return setsContains(listB, listA)
}
func estimateSetsCost(costFactor float64) checker.FunctionEstimator {
return func(estimator checker.CostEstimator, target *checker.AstNode, args []checker.AstNode) *checker.CallEstimate {
if len(args) != 2 {
return nil
}
arg0Size := estimateSize(estimator, args[0])
arg1Size := estimateSize(estimator, args[1])
costEstimate := arg0Size.Multiply(arg1Size).MultiplyByCostFactor(costFactor).Add(callCostEstimate)
return &checker.CallEstimate{CostEstimate: costEstimate}
}
}
func estimateSize(estimator checker.CostEstimator, node checker.AstNode) checker.SizeEstimate {
if l := node.ComputedSize(); l != nil {
return *l
}
if l := estimator.EstimateSize(node); l != nil {
return *l
}
return checker.SizeEstimate{Min: 0, Max: math.MaxUint64}
}
func trackSetsCost(costFactor float64) interpreter.FunctionTracker {
return func(args []ref.Val, _ ref.Val) *uint64 {
lhsSize := actualSize(args[0])
rhsSize := actualSize(args[1])
cost := callCost + uint64(float64(lhsSize*rhsSize)*costFactor)
return &cost
}
}
func actualSize(value ref.Val) uint64 {
if sz, ok := value.(traits.Sizer); ok {
return uint64(sz.Size().(types.Int))
}
return 1
}
var (
callCostEstimate = checker.FixedCostEstimate(1)
callCost = uint64(1)
)
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Package ext contains CEL extension libraries where each library defines a related set of
// constants, functions, macros, or other configuration settings which may not be covered by
// the core CEL spec.
package ext
import (
"fmt"
"math"
"reflect"
"strings"
"unicode"
"unicode/utf8"
"golang.org/x/text/language"
"github.com/google/cel-go/cel"
"github.com/google/cel-go/common/types"
"github.com/google/cel-go/common/types/ref"
"github.com/google/cel-go/common/types/traits"
)
const (
defaultLocale = "en-US"
defaultPrecision = 6
)
// Strings returns a cel.EnvOption to configure extended functions for string manipulation.
// As a general note, all indices are zero-based.
//
// # CharAt
//
// Returns the character at the given position. If the position is negative, or greater than
// the length of the string, the function will produce an error:
//
// <string>.charAt(<int>) -> <string>
//
// Examples:
//
// 'hello'.charAt(4) // return 'o'
// 'hello'.charAt(5) // return ''
// 'hello'.charAt(-1) // error
//
// # Format
//
// Introduced at version: 1
//
// Returns a new string with substitutions being performed, printf-style.
// The valid formatting clauses are:
//
// `%s` - substitutes a string. This can also be used on bools, lists, maps, bytes,
// Duration and Timestamp, in addition to all numerical types (int, uint, and double).
// Note that the dot/period decimal separator will always be used when printing a list
// or map that contains a double, and that null can be passed (which results in the
// string "null") in addition to types.
// `%d` - substitutes an integer.
// `%f` - substitutes a double with fixed-point precision. The default precision is 6, but
// this can be adjusted. The strings `Infinity`, `-Infinity`, and `NaN` are also valid input
// for this clause.
// `%e` - substitutes a double in scientific notation. The default precision is 6, but this
// can be adjusted.
// `%b` - substitutes an integer with its equivalent binary string. Can also be used on bools.
// `%x` - substitutes an integer with its equivalent in hexadecimal, or if given a string or
// bytes, will output each character's equivalent in hexadecimal.
// `%X` - same as above, but with A-F capitalized.
// `%o` - substitutes an integer with its equivalent in octal.
//
// <string>.format(<list>) -> <string>
//
// Examples:
//
// "this is a string: %s\nand an integer: %d".format(["str", 42]) // returns "this is a string: str\nand an integer: 42"
// "a double substituted with %%s: %s".format([64.2]) // returns "a double substituted with %s: 64.2"
// "string type: %s".format([type(string)]) // returns "string type: string"
// "timestamp: %s".format([timestamp("2023-02-03T23:31:20+00:00")]) // returns "timestamp: 2023-02-03T23:31:20Z"
// "duration: %s".format([duration("1h45m47s")]) // returns "duration: 6347s"
// "%f".format([3.14]) // returns "3.140000"
// "scientific notation: %e".format([2.71828]) // returns "scientific notation: 2.718280\u202f\u00d7\u202f10\u2070\u2070"
// "5 in binary: %b".format([5]), // returns "5 in binary; 101"
// "26 in hex: %x".format([26]), // returns "26 in hex: 1a"
// "26 in hex (uppercase): %X".format([26]) // returns "26 in hex (uppercase): 1A"
// "30 in octal: %o".format([30]) // returns "30 in octal: 36"
// "a map inside a list: %s".format([[1, 2, 3, {"a": "x", "b": "y", "c": "z"}]]) // returns "a map inside a list: [1, 2, 3, {"a":"x", "b":"y", "c":"d"}]"
// "true bool: %s - false bool: %s\nbinary bool: %b".format([true, false, true]) // returns "true bool: true - false bool: false\nbinary bool: 1"
//
// Passing an incorrect type (a string to `%b`) is considered an error, as well as attempting
// to use more formatting clauses than there are arguments (`%d %d %d` while passing two ints, for instance).
// If compile-time checking is enabled, and the formatting string is a constant, and the argument list is a literal,
// then letting any arguments go unused/unformatted is also considered an error.
//
// # IndexOf
//
// Returns the integer index of the first occurrence of the search string. If the search string is
// not found the function returns -1.
//
// The function also accepts an optional position from which to begin the substring search. If the
// substring is the empty string, the index where the search starts is returned (zero or custom).
//
// <string>.indexOf(<string>) -> <int>
// <string>.indexOf(<string>, <int>) -> <int>
//
// Examples:
//
// 'hello mellow'.indexOf('') // returns 0
// 'hello mellow'.indexOf('ello') // returns 1
// 'hello mellow'.indexOf('jello') // returns -1
// 'hello mellow'.indexOf('', 2) // returns 2
// 'hello mellow'.indexOf('ello', 2) // returns 7
// 'hello mellow'.indexOf('ello', 20) // returns -1
// 'hello mellow'.indexOf('ello', -1) // error
//
// # Join
//
// Returns a new string where the elements of string list are concatenated.
//
// The function also accepts an optional separator which is placed between elements in the resulting string.
//
// <list<string>>.join() -> <string>
// <list<string>>.join(<string>) -> <string>
//
// Examples:
//
// ['hello', 'mellow'].join() // returns 'hellomellow'
// ['hello', 'mellow'].join(' ') // returns 'hello mellow'
// [].join() // returns ''
// [].join('/') // returns ''
//
// # LastIndexOf
//
// Returns the integer index at the start of the last occurrence of the search string. If the
// search string is not found the function returns -1.
//
// The function also accepts an optional position which represents the last index to be
// considered as the beginning of the substring match. If the substring is the empty string,
// the index where the search starts is returned (string length or custom).
//
// <string>.lastIndexOf(<string>) -> <int>
// <string>.lastIndexOf(<string>, <int>) -> <int>
//
// Examples:
//
// 'hello mellow'.lastIndexOf('') // returns 12
// 'hello mellow'.lastIndexOf('ello') // returns 7
// 'hello mellow'.lastIndexOf('jello') // returns -1
// 'hello mellow'.lastIndexOf('ello', 6) // returns 1
// 'hello mellow'.lastIndexOf('ello', 20) // returns -1
// 'hello mellow'.lastIndexOf('ello', -1) // error
//
// # LowerAscii
//
// Returns a new string where all ASCII characters are lower-cased.
//
// This function does not perform Unicode case-mapping for characters outside the ASCII range.
//
// <string>.lowerAscii() -> <string>
//
// Examples:
//
// 'TacoCat'.lowerAscii() // returns 'tacocat'
// 'TacoCÆt Xii'.lowerAscii() // returns 'tacocÆt xii'
//
// # Strings.Quote
//
// Introduced in version: 1
//
// Takes the given string and makes it safe to print (without any formatting due to escape sequences).
// If any invalid UTF-8 characters are encountered, they are replaced with \uFFFD.
//
// strings.quote(<string>)
//
// Examples:
//
// strings.quote('single-quote with "double quote"') // returns '"single-quote with \"double quote\""'
// strings.quote("two escape sequences \a\n") // returns '"two escape sequences \\a\\n"'
//
// # Replace
//
// Returns a new string based on the target, which replaces the occurrences of a search string
// with a replacement string if present. The function accepts an optional limit on the number of
// substring replacements to be made.
//
// When the replacement limit is 0, the result is the original string. When the limit is a negative
// number, the function behaves the same as replace all.
//
// <string>.replace(<string>, <string>) -> <string>
// <string>.replace(<string>, <string>, <int>) -> <string>
//
// Examples:
//
// 'hello hello'.replace('he', 'we') // returns 'wello wello'
// 'hello hello'.replace('he', 'we', -1) // returns 'wello wello'
// 'hello hello'.replace('he', 'we', 1) // returns 'wello hello'
// 'hello hello'.replace('he', 'we', 0) // returns 'hello hello'
// 'hello hello'.replace('', '_') // returns '_h_e_l_l_o_ _h_e_l_l_o_'
// 'hello hello'.replace('h', '') // returns 'ello ello'
//
// # Split
//
// Returns a list of strings split from the input by the given separator. The function accepts
// an optional argument specifying a limit on the number of substrings produced by the split.
//
// When the split limit is 0, the result is an empty list. When the limit is 1, the result is the
// target string to split. When the limit is a negative number, the function behaves the same as
// split all.
//
// <string>.split(<string>) -> <list<string>>
// <string>.split(<string>, <int>) -> <list<string>>
//
// Examples:
//
// 'hello hello hello'.split(' ') // returns ['hello', 'hello', 'hello']
// 'hello hello hello'.split(' ', 0) // returns []
// 'hello hello hello'.split(' ', 1) // returns ['hello hello hello']
// 'hello hello hello'.split(' ', 2) // returns ['hello', 'hello hello']
// 'hello hello hello'.split(' ', -1) // returns ['hello', 'hello', 'hello']
//
// # Substring
//
// Returns the substring given a numeric range corresponding to character positions. Optionally
// may omit the trailing range for a substring from a given character position until the end of
// a string.
//
// Character offsets are 0-based with an inclusive start range and exclusive end range. It is an
// error to specify an end range that is lower than the start range, or for either the start or end
// index to be negative or exceed the string length.
//
// <string>.substring(<int>) -> <string>
// <string>.substring(<int>, <int>) -> <string>
//
// Examples:
//
// 'tacocat'.substring(4) // returns 'cat'
// 'tacocat'.substring(0, 4) // returns 'taco'
// 'tacocat'.substring(-1) // error
// 'tacocat'.substring(2, 1) // error
//
// # Trim
//
// Returns a new string which removes the leading and trailing whitespace in the target string.
// The trim function uses the Unicode definition of whitespace which does not include the
// zero-width spaces. See: https://en.wikipedia.org/wiki/Whitespace_character#Unicode
//
// <string>.trim() -> <string>
//
// Examples:
//
// ' \ttrim\n '.trim() // returns 'trim'
//
// # UpperAscii
//
// Returns a new string where all ASCII characters are upper-cased.
//
// This function does not perform Unicode case-mapping for characters outside the ASCII range.
//
// <string>.upperAscii() -> <string>
//
// Examples:
//
// 'TacoCat'.upperAscii() // returns 'TACOCAT'
// 'TacoCÆt Xii'.upperAscii() // returns 'TACOCÆT XII'
//
// # Reverse
//
// Introduced at version: 3
//
// Returns a new string whose characters are the same as the target string, only formatted in
// reverse order.
// This function relies on converting strings to rune arrays in order to reverse
//
// <string>.reverse() -> <string>
//
// Examples:
//
// 'gums'.reverse() // returns 'smug'
// 'John Smith'.reverse() // returns 'htimS nhoJ'
//
// Introduced at version: 4
//
// Formatting updated to adhere to https://github.com/google/cel-spec/blob/master/doc/extensions/strings.md.
//
// <string>.format(<list>) -> <string>
func Strings(options ...StringsOption) cel.EnvOption {
s := &stringLib{
version: math.MaxUint32,
}
for _, o := range options {
s = o(s)
}
return cel.Lib(s)
}
type stringLib struct {
locale string
version uint32
}
// LibraryName implements the SingletonLibrary interface method.
func (*stringLib) LibraryName() string {
return "cel.lib.ext.strings"
}
// StringsOption is a functional interface for configuring the strings library.
type StringsOption func(*stringLib) *stringLib
// StringsLocale configures the library with the given locale. The locale tag will
// be checked for validity at the time that EnvOptions are configured. If this option
// is not passed, string.format will behave as if en_US was passed as the locale.
//
// If StringsVersion is greater than or equal to 4, this option is ignored.
func StringsLocale(locale string) StringsOption {
return func(sl *stringLib) *stringLib {
sl.locale = locale
return sl
}
}
// StringsVersion configures the version of the string library.
//
// The version limits which functions are available. Only functions introduced
// below or equal to the given version included in the library. If this option
// is not set, all functions are available.
//
// See the library documentation to determine which version a function was introduced.
// If the documentation does not state which version a function was introduced, it can
// be assumed to be introduced at version 0, when the library was first created.
func StringsVersion(version uint32) StringsOption {
return func(lib *stringLib) *stringLib {
lib.version = version
return lib
}
}
// StringsValidateFormatCalls validates type-checked ASTs to ensure that string.format() calls have
// valid formatting clauses and valid argument types for each clause.
//
// Deprecated
func StringsValidateFormatCalls(value bool) StringsOption {
return func(s *stringLib) *stringLib {
return s
}
}
// CompileOptions implements the Library interface method.
func (lib *stringLib) CompileOptions() []cel.EnvOption {
formatLocale := "en_US"
if lib.version < 4 && lib.locale != "" {
// ensure locale is properly-formed if set
_, err := language.Parse(lib.locale)
if err != nil {
return []cel.EnvOption{
func(e *cel.Env) (*cel.Env, error) {
return nil, fmt.Errorf("failed to parse locale: %w", err)
},
}
}
formatLocale = lib.locale
}
opts := []cel.EnvOption{
cel.Function("charAt",
cel.MemberOverload("string_char_at_int", []*cel.Type{cel.StringType, cel.IntType}, cel.StringType,
cel.BinaryBinding(func(str, ind ref.Val) ref.Val {
s := str.(types.String)
i := ind.(types.Int)
return stringOrError(charAt(string(s), int64(i)))
}))),
cel.Function("indexOf",
cel.MemberOverload("string_index_of_string", []*cel.Type{cel.StringType, cel.StringType}, cel.IntType,
cel.BinaryBinding(func(str, substr ref.Val) ref.Val {
s := str.(types.String)
sub := substr.(types.String)
return intOrError(indexOf(string(s), string(sub)))
})),
cel.MemberOverload("string_index_of_string_int", []*cel.Type{cel.StringType, cel.StringType, cel.IntType}, cel.IntType,
cel.FunctionBinding(func(args ...ref.Val) ref.Val {
s := args[0].(types.String)
sub := args[1].(types.String)
offset := args[2].(types.Int)
return intOrError(indexOfOffset(string(s), string(sub), int64(offset)))
}))),
cel.Function("lastIndexOf",
cel.MemberOverload("string_last_index_of_string", []*cel.Type{cel.StringType, cel.StringType}, cel.IntType,
cel.BinaryBinding(func(str, substr ref.Val) ref.Val {
s := str.(types.String)
sub := substr.(types.String)
return intOrError(lastIndexOf(string(s), string(sub)))
})),
cel.MemberOverload("string_last_index_of_string_int", []*cel.Type{cel.StringType, cel.StringType, cel.IntType}, cel.IntType,
cel.FunctionBinding(func(args ...ref.Val) ref.Val {
s := args[0].(types.String)
sub := args[1].(types.String)
offset := args[2].(types.Int)
return intOrError(lastIndexOfOffset(string(s), string(sub), int64(offset)))
}))),
cel.Function("lowerAscii",
cel.MemberOverload("string_lower_ascii", []*cel.Type{cel.StringType}, cel.StringType,
cel.UnaryBinding(func(str ref.Val) ref.Val {
s := str.(types.String)
return stringOrError(lowerASCII(string(s)))
}))),
cel.Function("replace",
cel.MemberOverload(
"string_replace_string_string", []*cel.Type{cel.StringType, cel.StringType, cel.StringType}, cel.StringType,
cel.FunctionBinding(func(args ...ref.Val) ref.Val {
str := args[0].(types.String)
old := args[1].(types.String)
new := args[2].(types.String)
return stringOrError(replace(string(str), string(old), string(new)))
})),
cel.MemberOverload(
"string_replace_string_string_int", []*cel.Type{cel.StringType, cel.StringType, cel.StringType, cel.IntType}, cel.StringType,
cel.FunctionBinding(func(args ...ref.Val) ref.Val {
str := args[0].(types.String)
old := args[1].(types.String)
new := args[2].(types.String)
n := args[3].(types.Int)
return stringOrError(replaceN(string(str), string(old), string(new), int64(n)))
}))),
cel.Function("split",
cel.MemberOverload("string_split_string", []*cel.Type{cel.StringType, cel.StringType}, cel.ListType(cel.StringType),
cel.BinaryBinding(func(str, separator ref.Val) ref.Val {
s := str.(types.String)
sep := separator.(types.String)
return listStringOrError(split(string(s), string(sep)))
})),
cel.MemberOverload("string_split_string_int", []*cel.Type{cel.StringType, cel.StringType, cel.IntType}, cel.ListType(cel.StringType),
cel.FunctionBinding(func(args ...ref.Val) ref.Val {
s := args[0].(types.String)
sep := args[1].(types.String)
n := args[2].(types.Int)
return listStringOrError(splitN(string(s), string(sep), int64(n)))
}))),
cel.Function("substring",
cel.MemberOverload("string_substring_int", []*cel.Type{cel.StringType, cel.IntType}, cel.StringType,
cel.BinaryBinding(func(str, offset ref.Val) ref.Val {
s := str.(types.String)
off := offset.(types.Int)
return stringOrError(substr(string(s), int64(off)))
})),
cel.MemberOverload("string_substring_int_int", []*cel.Type{cel.StringType, cel.IntType, cel.IntType}, cel.StringType,
cel.FunctionBinding(func(args ...ref.Val) ref.Val {
s := args[0].(types.String)
start := args[1].(types.Int)
end := args[2].(types.Int)
return stringOrError(substrRange(string(s), int64(start), int64(end)))
}))),
cel.Function("trim",
cel.MemberOverload("string_trim", []*cel.Type{cel.StringType}, cel.StringType,
cel.UnaryBinding(func(str ref.Val) ref.Val {
s := str.(types.String)
return stringOrError(trimSpace(string(s)))
}))),
cel.Function("upperAscii",
cel.MemberOverload("string_upper_ascii", []*cel.Type{cel.StringType}, cel.StringType,
cel.UnaryBinding(func(str ref.Val) ref.Val {
s := str.(types.String)
return stringOrError(upperASCII(string(s)))
}))),
}
if lib.version >= 1 {
if lib.version >= 4 {
opts = append(opts, cel.Function("format",
cel.MemberOverload("string_format", []*cel.Type{cel.StringType, cel.ListType(cel.DynType)}, cel.StringType,
cel.FunctionBinding(func(args ...ref.Val) ref.Val {
s := string(args[0].(types.String))
formatArgs := args[1].(traits.Lister)
return stringOrError(parseFormatStringV2(s, &stringFormatterV2{}, &stringArgList{formatArgs}))
}))))
} else {
opts = append(opts, cel.Function("format",
cel.MemberOverload("string_format", []*cel.Type{cel.StringType, cel.ListType(cel.DynType)}, cel.StringType,
cel.FunctionBinding(func(args ...ref.Val) ref.Val {
s := string(args[0].(types.String))
formatArgs := args[1].(traits.Lister)
return stringOrError(parseFormatString(s, &stringFormatter{}, &stringArgList{formatArgs}, formatLocale))
}))))
}
opts = append(opts,
cel.Function("strings.quote", cel.Overload("strings_quote", []*cel.Type{cel.StringType}, cel.StringType,
cel.UnaryBinding(func(str ref.Val) ref.Val {
s := str.(types.String)
return stringOrError(quote(string(s)))
}))))
}
if lib.version >= 2 {
opts = append(opts,
cel.Function("join",
cel.MemberOverload("list_join", []*cel.Type{cel.ListType(cel.StringType)}, cel.StringType,
cel.UnaryBinding(func(list ref.Val) ref.Val {
l := list.(traits.Lister)
return stringOrError(joinValSeparator(l, ""))
})),
cel.MemberOverload("list_join_string", []*cel.Type{cel.ListType(cel.StringType), cel.StringType}, cel.StringType,
cel.BinaryBinding(func(list, delim ref.Val) ref.Val {
l := list.(traits.Lister)
d := delim.(types.String)
return stringOrError(joinValSeparator(l, string(d)))
}))),
)
} else {
opts = append(opts,
cel.Function("join",
cel.MemberOverload("list_join", []*cel.Type{cel.ListType(cel.StringType)}, cel.StringType,
cel.UnaryBinding(func(list ref.Val) ref.Val {
l, err := list.ConvertToNative(stringListType)
if err != nil {
return types.WrapErr(err)
}
return stringOrError(join(l.([]string)))
})),
cel.MemberOverload("list_join_string", []*cel.Type{cel.ListType(cel.StringType), cel.StringType}, cel.StringType,
cel.BinaryBinding(func(list, delim ref.Val) ref.Val {
l, err := list.ConvertToNative(stringListType)
if err != nil {
return types.WrapErr(err)
}
d := delim.(types.String)
return stringOrError(joinSeparator(l.([]string), string(d)))
}))),
)
}
if lib.version >= 3 {
opts = append(opts,
cel.Function("reverse",
cel.MemberOverload("string_reverse", []*cel.Type{cel.StringType}, cel.StringType,
cel.UnaryBinding(func(str ref.Val) ref.Val {
s := str.(types.String)
return stringOrError(reverse(string(s)))
}))),
)
}
if lib.version >= 1 {
if lib.version >= 4 {
opts = append(opts, cel.ASTValidators(stringFormatValidatorV2{}))
} else {
opts = append(opts, cel.ASTValidators(stringFormatValidator{}))
}
}
return opts
}
// ProgramOptions implements the Library interface method.
func (*stringLib) ProgramOptions() []cel.ProgramOption {
return []cel.ProgramOption{}
}
func charAt(str string, ind int64) (string, error) {
i := int(ind)
runes := []rune(str)
if i < 0 || i > len(runes) {
return "", fmt.Errorf("index out of range: %d", ind)
}
if i == len(runes) {
return "", nil
}
return string(runes[i]), nil
}
func indexOf(str, substr string) (int64, error) {
return indexOfOffset(str, substr, int64(0))
}
func indexOfOffset(str, substr string, offset int64) (int64, error) {
if substr == "" {
return offset, nil
}
off := int(offset)
runes := []rune(str)
subrunes := []rune(substr)
if off < 0 {
return -1, fmt.Errorf("index out of range: %d", off)
}
// If the offset exceeds the length, return -1 rather than error.
if off >= len(runes) {
return -1, nil
}
for i := off; i < len(runes)-(len(subrunes)-1); i++ {
found := true
for j := 0; j < len(subrunes); j++ {
if runes[i+j] != subrunes[j] {
found = false
break
}
}
if found {
return int64(i), nil
}
}
return -1, nil
}
func lastIndexOf(str, substr string) (int64, error) {
runes := []rune(str)
if substr == "" {
return int64(len(runes)), nil
}
if len(str) < len(substr) {
return -1, nil
}
return lastIndexOfOffset(str, substr, int64(len(runes)-1))
}
func lastIndexOfOffset(str, substr string, offset int64) (int64, error) {
if substr == "" {
return offset, nil
}
off := int(offset)
runes := []rune(str)
subrunes := []rune(substr)
if off < 0 {
return -1, fmt.Errorf("index out of range: %d", off)
}
// If the offset is far greater than the length return -1
if off >= len(runes) {
return -1, nil
}
if off > len(runes)-len(subrunes) {
off = len(runes) - len(subrunes)
}
for i := off; i >= 0; i-- {
found := true
for j := 0; j < len(subrunes); j++ {
if runes[i+j] != subrunes[j] {
found = false
break
}
}
if found {
return int64(i), nil
}
}
return -1, nil
}
func lowerASCII(str string) (string, error) {
runes := []rune(str)
for i, r := range runes {
if r <= unicode.MaxASCII {
r = unicode.ToLower(r)
runes[i] = r
}
}
return string(runes), nil
}
func replace(str, old, new string) (string, error) {
return strings.ReplaceAll(str, old, new), nil
}
func replaceN(str, old, new string, n int64) (string, error) {
return strings.Replace(str, old, new, int(n)), nil
}
func split(str, sep string) ([]string, error) {
return strings.Split(str, sep), nil
}
func splitN(str, sep string, n int64) ([]string, error) {
return strings.SplitN(str, sep, int(n)), nil
}
func substr(str string, start int64) (string, error) {
runes := []rune(str)
if int(start) < 0 || int(start) > len(runes) {
return "", fmt.Errorf("index out of range: %d", start)
}
return string(runes[start:]), nil
}
func substrRange(str string, start, end int64) (string, error) {
runes := []rune(str)
l := len(runes)
if start > end {
return "", fmt.Errorf("invalid substring range. start: %d, end: %d", start, end)
}
if int(start) < 0 || int(start) > l {
return "", fmt.Errorf("index out of range: %d", start)
}
if int(end) < 0 || int(end) > l {
return "", fmt.Errorf("index out of range: %d", end)
}
return string(runes[int(start):int(end)]), nil
}
func trimSpace(str string) (string, error) {
return strings.TrimSpace(str), nil
}
func upperASCII(str string) (string, error) {
runes := []rune(str)
for i, r := range runes {
if r <= unicode.MaxASCII {
r = unicode.ToUpper(r)
runes[i] = r
}
}
return string(runes), nil
}
func reverse(str string) (string, error) {
chars := []rune(str)
for i, j := 0, len(chars)-1; i < j; i, j = i+1, j-1 {
chars[i], chars[j] = chars[j], chars[i]
}
return string(chars), nil
}
func joinSeparator(strs []string, separator string) (string, error) {
return strings.Join(strs, separator), nil
}
func join(strs []string) (string, error) {
return strings.Join(strs, ""), nil
}
func joinValSeparator(strs traits.Lister, separator string) (string, error) {
sz := strs.Size().(types.Int)
var sb strings.Builder
for i := types.Int(0); i < sz; i++ {
if i != 0 {
sb.WriteString(separator)
}
elem := strs.Get(i)
str, ok := elem.(types.String)
if !ok {
return "", fmt.Errorf("join: invalid input: %v", elem)
}
sb.WriteString(string(str))
}
return sb.String(), nil
}
// quote implements a string quoting function. The string will be wrapped in
// double quotes, and all valid CEL escape sequences will be escaped to show up
// literally if printed. If the input contains any invalid UTF-8, the invalid runes
// will be replaced with utf8.RuneError.
func quote(s string) (string, error) {
var quotedStrBuilder strings.Builder
for _, c := range sanitize(s) {
switch c {
case '\a':
quotedStrBuilder.WriteString("\\a")
case '\b':
quotedStrBuilder.WriteString("\\b")
case '\f':
quotedStrBuilder.WriteString("\\f")
case '\n':
quotedStrBuilder.WriteString("\\n")
case '\r':
quotedStrBuilder.WriteString("\\r")
case '\t':
quotedStrBuilder.WriteString("\\t")
case '\v':
quotedStrBuilder.WriteString("\\v")
case '\\':
quotedStrBuilder.WriteString("\\\\")
case '"':
quotedStrBuilder.WriteString("\\\"")
default:
quotedStrBuilder.WriteRune(c)
}
}
escapedStr := quotedStrBuilder.String()
return "\"" + escapedStr + "\"", nil
}
// sanitize replaces all invalid runes in the given string with utf8.RuneError.
func sanitize(s string) string {
var sanitizedStringBuilder strings.Builder
for _, r := range s {
if !utf8.ValidRune(r) {
sanitizedStringBuilder.WriteRune(utf8.RuneError)
} else {
sanitizedStringBuilder.WriteRune(r)
}
}
return sanitizedStringBuilder.String()
}
var (
stringListType = reflect.TypeOf([]string{})
)
// Copyright 2018 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package interpreter
import (
"errors"
"fmt"
"github.com/google/cel-go/common/types/ref"
)
// Activation used to resolve identifiers by name and references by id.
//
// An Activation is the primary mechanism by which a caller supplies input into a CEL program.
type Activation interface {
// ResolveName returns a value from the activation by qualified name, or false if the name
// could not be found.
ResolveName(name string) (any, bool)
// Parent returns the parent of the current activation, may be nil.
// If non-nil, the parent will be searched during resolve calls.
Parent() Activation
}
// EmptyActivation returns a variable-free activation.
func EmptyActivation() Activation {
return emptyActivation{}
}
// emptyActivation is a variable-free activation.
type emptyActivation struct{}
func (emptyActivation) ResolveName(string) (any, bool) { return nil, false }
func (emptyActivation) Parent() Activation { return nil }
// NewActivation returns an activation based on a map-based binding where the map keys are
// expected to be qualified names used with ResolveName calls.
//
// The input `bindings` may either be of type `Activation` or `map[string]any`.
//
// Lazy bindings may be supplied within the map-based input in either of the following forms:
// - func() any
// - func() ref.Val
//
// The output of the lazy binding will overwrite the variable reference in the internal map.
//
// Values which are not represented as ref.Val types on input may be adapted to a ref.Val using
// the types.Adapter configured in the environment.
func NewActivation(bindings any) (Activation, error) {
if bindings == nil {
return nil, errors.New("bindings must be non-nil")
}
a, isActivation := bindings.(Activation)
if isActivation {
return a, nil
}
m, isMap := bindings.(map[string]any)
if !isMap {
return nil, fmt.Errorf(
"activation input must be an activation or map[string]interface: got %T",
bindings)
}
return &mapActivation{bindings: m}, nil
}
// mapActivation which implements Activation and maps of named values.
//
// Named bindings may lazily supply values by providing a function which accepts no arguments and
// produces an interface value.
type mapActivation struct {
bindings map[string]any
}
// Parent implements the Activation interface method.
func (a *mapActivation) Parent() Activation {
return nil
}
// ResolveName implements the Activation interface method.
func (a *mapActivation) ResolveName(name string) (any, bool) {
obj, found := a.bindings[name]
if !found {
return nil, false
}
fn, isLazy := obj.(func() ref.Val)
if isLazy {
obj = fn()
a.bindings[name] = obj
}
fnRaw, isLazy := obj.(func() any)
if isLazy {
obj = fnRaw()
a.bindings[name] = obj
}
return obj, found
}
// hierarchicalActivation which implements Activation and contains a parent and
// child activation.
type hierarchicalActivation struct {
parent Activation
child Activation
}
// Parent implements the Activation interface method.
func (a *hierarchicalActivation) Parent() Activation {
return a.parent
}
// ResolveName implements the Activation interface method.
func (a *hierarchicalActivation) ResolveName(name string) (any, bool) {
if object, found := a.child.ResolveName(name); found {
return object, found
}
return a.parent.ResolveName(name)
}
// NewHierarchicalActivation takes two activations and produces a new one which prioritizes
// resolution in the child first and parent(s) second.
func NewHierarchicalActivation(parent Activation, child Activation) Activation {
return &hierarchicalActivation{parent, child}
}
// NewPartialActivation returns an Activation which contains a list of AttributePattern values
// representing field and index operations that should result in a 'types.Unknown' result.
//
// The `bindings` value may be any value type supported by the interpreter.NewActivation call,
// but is typically either an existing Activation or map[string]any.
func NewPartialActivation(bindings any,
unknowns ...*AttributePattern) (PartialActivation, error) {
a, err := NewActivation(bindings)
if err != nil {
return nil, err
}
return &partActivation{Activation: a, unknowns: unknowns}, nil
}
// PartialActivation extends the Activation interface with a set of UnknownAttributePatterns.
type PartialActivation interface {
Activation
// UnknownAttributePaths returns a set of AttributePattern values which match Attribute
// expressions for data accesses whose values are not yet known.
UnknownAttributePatterns() []*AttributePattern
}
// partialActivationConverter indicates whether an Activation implementation supports conversion to a PartialActivation
type partialActivationConverter interface {
// AsPartialActivation converts the current activation to a PartialActivation
AsPartialActivation() (PartialActivation, bool)
}
// partActivation is the default implementations of the PartialActivation interface.
type partActivation struct {
Activation
unknowns []*AttributePattern
}
// UnknownAttributePatterns implements the PartialActivation interface method.
func (a *partActivation) UnknownAttributePatterns() []*AttributePattern {
return a.unknowns
}
// AsPartialActivation returns the partActivation as a PartialActivation interface.
func (a *partActivation) AsPartialActivation() (PartialActivation, bool) {
return a, true
}
// AsPartialActivation walks the activation hierarchy and returns the first PartialActivation, if found.
func AsPartialActivation(vars Activation) (PartialActivation, bool) {
// Only internal activation instances may implement this interface
if pv, ok := vars.(partialActivationConverter); ok {
return pv.AsPartialActivation()
}
// Since Activations may be hierarchical, test whether a parent converts to a PartialActivation
if vars.Parent() != nil {
return AsPartialActivation(vars.Parent())
}
return nil, false
}
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package interpreter
import (
"fmt"
"github.com/google/cel-go/common/containers"
"github.com/google/cel-go/common/types"
"github.com/google/cel-go/common/types/ref"
)
// AttributePattern represents a top-level variable with an optional set of qualifier patterns.
//
// When using a CEL expression within a container, e.g. a package or namespace, the variable name
// in the pattern must match the qualified name produced during the variable namespace resolution.
// For example, if variable `c` appears in an expression whose container is `a.b`, the variable
// name supplied to the pattern must be `a.b.c`
//
// The qualifier patterns for attribute matching must be one of the following:
//
// - valid map key type: string, int, uint, bool
// - wildcard (*)
//
// Examples:
//
// 1. ns.myvar["complex-value"]
// 2. ns.myvar["complex-value"][0]
// 3. ns.myvar["complex-value"].*.name
//
// The first example is simple: match an attribute where the variable is 'ns.myvar' with a
// field access on 'complex-value'. The second example expands the match to indicate that only
// a specific index `0` should match. And lastly, the third example matches any indexed access
// that later selects the 'name' field.
type AttributePattern struct {
variable string
qualifierPatterns []*AttributeQualifierPattern
}
// NewAttributePattern produces a new mutable AttributePattern based on a variable name.
func NewAttributePattern(variable string) *AttributePattern {
return &AttributePattern{
variable: variable,
qualifierPatterns: []*AttributeQualifierPattern{},
}
}
// QualString adds a string qualifier pattern to the AttributePattern. The string may be a valid
// identifier, or string map key including empty string.
func (apat *AttributePattern) QualString(pattern string) *AttributePattern {
apat.qualifierPatterns = append(apat.qualifierPatterns,
&AttributeQualifierPattern{value: pattern})
return apat
}
// QualInt adds an int qualifier pattern to the AttributePattern. The index may be either a map or
// list index.
func (apat *AttributePattern) QualInt(pattern int64) *AttributePattern {
apat.qualifierPatterns = append(apat.qualifierPatterns,
&AttributeQualifierPattern{value: pattern})
return apat
}
// QualUint adds an uint qualifier pattern for a map index operation to the AttributePattern.
func (apat *AttributePattern) QualUint(pattern uint64) *AttributePattern {
apat.qualifierPatterns = append(apat.qualifierPatterns,
&AttributeQualifierPattern{value: pattern})
return apat
}
// QualBool adds a bool qualifier pattern for a map index operation to the AttributePattern.
func (apat *AttributePattern) QualBool(pattern bool) *AttributePattern {
apat.qualifierPatterns = append(apat.qualifierPatterns,
&AttributeQualifierPattern{value: pattern})
return apat
}
// Wildcard adds a special sentinel qualifier pattern that will match any single qualifier.
func (apat *AttributePattern) Wildcard() *AttributePattern {
apat.qualifierPatterns = append(apat.qualifierPatterns,
&AttributeQualifierPattern{wildcard: true})
return apat
}
// VariableMatches returns true if the fully qualified variable matches the AttributePattern
// fully qualified variable name.
func (apat *AttributePattern) VariableMatches(variable string) bool {
return apat.variable == variable
}
// QualifierPatterns returns the set of AttributeQualifierPattern values on the AttributePattern.
func (apat *AttributePattern) QualifierPatterns() []*AttributeQualifierPattern {
return apat.qualifierPatterns
}
// AttributeQualifierPattern holds a wildcard or valued qualifier pattern.
type AttributeQualifierPattern struct {
wildcard bool
value any
}
// Matches returns true if the qualifier pattern is a wildcard, or the Qualifier implements the
// qualifierValueEquator interface and its IsValueEqualTo returns true for the qualifier pattern.
func (qpat *AttributeQualifierPattern) Matches(q Qualifier) bool {
if qpat.wildcard {
return true
}
qve, ok := q.(qualifierValueEquator)
return ok && qve.QualifierValueEquals(qpat.value)
}
// qualifierValueEquator defines an interface for determining if an input value, of valid map key
// type, is equal to the value held in the Qualifier. This interface is used by the
// AttributeQualifierPattern to determine pattern matches for non-wildcard qualifier patterns.
//
// Note: Attribute values are also Qualifier values; however, Attributes are resolved before
// qualification happens. This is an implementation detail, but one relevant to why the Attribute
// types do not surface in the list of implementations.
//
// See: partialAttributeFactory.matchesUnknownPatterns for more details on how this interface is
// used.
type qualifierValueEquator interface {
// QualifierValueEquals returns true if the input value is equal to the value held in the
// Qualifier.
QualifierValueEquals(value any) bool
}
// QualifierValueEquals implementation for boolean qualifiers.
func (q *boolQualifier) QualifierValueEquals(value any) bool {
bval, ok := value.(bool)
return ok && q.value == bval
}
// QualifierValueEquals implementation for field qualifiers.
func (q *fieldQualifier) QualifierValueEquals(value any) bool {
sval, ok := value.(string)
return ok && q.Name == sval
}
// QualifierValueEquals implementation for string qualifiers.
func (q *stringQualifier) QualifierValueEquals(value any) bool {
sval, ok := value.(string)
return ok && q.value == sval
}
// QualifierValueEquals implementation for int qualifiers.
func (q *intQualifier) QualifierValueEquals(value any) bool {
return numericValueEquals(value, q.celValue)
}
// QualifierValueEquals implementation for uint qualifiers.
func (q *uintQualifier) QualifierValueEquals(value any) bool {
return numericValueEquals(value, q.celValue)
}
// QualifierValueEquals implementation for double qualifiers.
func (q *doubleQualifier) QualifierValueEquals(value any) bool {
return numericValueEquals(value, q.celValue)
}
// numericValueEquals uses CEL equality to determine whether two number values are
func numericValueEquals(value any, celValue ref.Val) bool {
val := types.DefaultTypeAdapter.NativeToValue(value)
return celValue.Equal(val) == types.True
}
// NewPartialAttributeFactory returns an AttributeFactory implementation capable of performing
// AttributePattern matches with PartialActivation inputs.
func NewPartialAttributeFactory(container *containers.Container, adapter types.Adapter, provider types.Provider, opts ...AttrFactoryOption) AttributeFactory {
fac := NewAttributeFactory(container, adapter, provider, opts...)
return &partialAttributeFactory{
AttributeFactory: fac,
container: container,
adapter: adapter,
provider: provider,
}
}
type partialAttributeFactory struct {
AttributeFactory
container *containers.Container
adapter types.Adapter
provider types.Provider
}
// AbsoluteAttribute implementation of the AttributeFactory interface which wraps the
// NamespacedAttribute resolution in an internal attributeMatcher object to dynamically match
// unknown patterns from PartialActivation inputs if given.
func (fac *partialAttributeFactory) AbsoluteAttribute(id int64, names ...string) NamespacedAttribute {
attr := fac.AttributeFactory.AbsoluteAttribute(id, names...)
return &attributeMatcher{fac: fac, NamespacedAttribute: attr}
}
// MaybeAttribute implementation of the AttributeFactory interface which ensure that the set of
// 'maybe' NamespacedAttribute values are produced using the partialAttributeFactory rather than
// the base AttributeFactory implementation.
func (fac *partialAttributeFactory) MaybeAttribute(id int64, name string) Attribute {
return &maybeAttribute{
id: id,
attrs: []NamespacedAttribute{
fac.AbsoluteAttribute(id, fac.container.ResolveCandidateNames(name)...),
},
adapter: fac.adapter,
provider: fac.provider,
fac: fac,
}
}
// matchesUnknownPatterns returns true if the variable names and qualifiers for a given
// Attribute value match any of the ActivationPattern objects in the set of unknown activation
// patterns on the given PartialActivation.
//
// For example, in the expression `a.b`, the Attribute is composed of variable `a`, with string
// qualifier `b`. When a PartialActivation is supplied, it indicates that some or all of the data
// provided in the input is unknown by specifying unknown AttributePatterns. An AttributePattern
// that refers to variable `a` with a string qualifier of `c` will not match `a.b`; however, any
// of the following patterns will match Attribute `a.b`:
//
// - `AttributePattern("a")`
// - `AttributePattern("a").Wildcard()`
// - `AttributePattern("a").QualString("b")`
// - `AttributePattern("a").QualString("b").QualInt(0)`
//
// Any AttributePattern which overlaps an Attribute or vice-versa will produce an Unknown result
// for the last pattern matched variable or qualifier in the Attribute. In the first matching
// example, the expression id representing variable `a` would be listed in the Unknown result,
// whereas in the other pattern examples, the qualifier `b` would be returned as the Unknown.
func (fac *partialAttributeFactory) matchesUnknownPatterns(
vars PartialActivation,
attrID int64,
variableNames []string,
qualifiers []Qualifier) (*types.Unknown, error) {
patterns := vars.UnknownAttributePatterns()
candidateIndices := map[int]struct{}{}
for _, variable := range variableNames {
for i, pat := range patterns {
if pat.VariableMatches(variable) {
if len(qualifiers) == 0 {
return types.NewUnknown(attrID, types.NewAttributeTrail(variable)), nil
}
candidateIndices[i] = struct{}{}
}
}
}
// Determine whether to return early if there are no candidate unknown patterns.
if len(candidateIndices) == 0 {
return nil, nil
}
// Resolve the attribute qualifiers into a static set. This prevents more dynamic
// Attribute resolutions than necessary when there are multiple unknown patterns
// that traverse the same Attribute-based qualifier field.
newQuals := make([]Qualifier, len(qualifiers))
for i, qual := range qualifiers {
attr, isAttr := qual.(Attribute)
if isAttr {
val, err := attr.Resolve(vars)
if err != nil {
return nil, err
}
// If this resolution behavior ever changes, new implementations of the
// qualifierValueEquator may be required to handle proper resolution.
qual, err = fac.NewQualifier(nil, qual.ID(), val, attr.IsOptional())
if err != nil {
return nil, err
}
}
newQuals[i] = qual
}
// Determine whether any of the unknown patterns match.
for patIdx := range candidateIndices {
pat := patterns[patIdx]
isUnk := true
matchExprID := attrID
qualPats := pat.QualifierPatterns()
for i, qual := range newQuals {
if i >= len(qualPats) {
break
}
matchExprID = qual.ID()
qualPat := qualPats[i]
// Note, the AttributeQualifierPattern relies on the input Qualifier not being an
// Attribute, since there is no way to resolve the Attribute with the information
// provided to the Matches call.
if !qualPat.Matches(qual) {
isUnk = false
break
}
}
if isUnk {
attr := types.NewAttributeTrail(pat.variable)
for i := 0; i < len(qualPats) && i < len(newQuals); i++ {
if qual, ok := newQuals[i].(ConstantQualifier); ok {
switch v := qual.Value().Value().(type) {
case bool:
types.QualifyAttribute[bool](attr, v)
case float64:
types.QualifyAttribute[int64](attr, int64(v))
case int64:
types.QualifyAttribute[int64](attr, v)
case string:
types.QualifyAttribute[string](attr, v)
case uint64:
types.QualifyAttribute[uint64](attr, v)
default:
types.QualifyAttribute[string](attr, fmt.Sprintf("%v", v))
}
} else {
types.QualifyAttribute[string](attr, "*")
}
}
return types.NewUnknown(matchExprID, attr), nil
}
}
return nil, nil
}
// attributeMatcher embeds the NamespacedAttribute interface which allows it to participate in
// AttributePattern matching against Attribute values without having to modify the code paths that
// identify Attributes in expressions.
type attributeMatcher struct {
NamespacedAttribute
qualifiers []Qualifier
fac *partialAttributeFactory
}
// AddQualifier implements the Attribute interface method.
func (m *attributeMatcher) AddQualifier(qual Qualifier) (Attribute, error) {
// Add the qualifier to the embedded NamespacedAttribute. If the input to the Resolve
// method is not a PartialActivation, or does not match an unknown attribute pattern, the
// Resolve method is directly invoked on the underlying NamespacedAttribute.
_, err := m.NamespacedAttribute.AddQualifier(qual)
if err != nil {
return nil, err
}
// The attributeMatcher overloads TryResolve and will attempt to match unknown patterns against
// the variable name and qualifier set contained within the Attribute. These values are not
// directly inspectable on the top-level NamespacedAttribute interface and so are tracked within
// the attributeMatcher.
m.qualifiers = append(m.qualifiers, qual)
return m, nil
}
// Resolve is an implementation of the NamespacedAttribute interface method which tests
// for matching unknown attribute patterns and returns types.Unknown if present. Otherwise,
// the standard Resolve logic applies.
func (m *attributeMatcher) Resolve(vars Activation) (any, error) {
id := m.NamespacedAttribute.ID()
// Bug in how partial activation is resolved, should search parents as well.
partial, isPartial := AsPartialActivation(vars)
if isPartial {
unk, err := m.fac.matchesUnknownPatterns(
partial,
id,
m.CandidateVariableNames(),
m.qualifiers)
if err != nil {
return nil, err
}
if unk != nil {
return unk, nil
}
}
return m.NamespacedAttribute.Resolve(vars)
}
// Qualify is an implementation of the Qualifier interface method.
func (m *attributeMatcher) Qualify(vars Activation, obj any) (any, error) {
return attrQualify(m.fac, vars, obj, m)
}
// QualifyIfPresent is an implementation of the Qualifier interface method.
func (m *attributeMatcher) QualifyIfPresent(vars Activation, obj any, presenceOnly bool) (any, bool, error) {
return attrQualifyIfPresent(m.fac, vars, obj, m, presenceOnly)
}
// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package interpreter
import (
"fmt"
"strings"
"github.com/google/cel-go/common/containers"
"github.com/google/cel-go/common/types"
"github.com/google/cel-go/common/types/ref"
"github.com/google/cel-go/common/types/traits"
)
// AttributeFactory provides methods creating Attribute and Qualifier values.
type AttributeFactory interface {
// AbsoluteAttribute creates an attribute that refers to a top-level variable name.
//
// Checked expressions generate absolute attribute with a single name.
// Parse-only expressions may have more than one possible absolute identifier when the
// expression is created within a container, e.g. package or namespace.
//
// When there is more than one name supplied to the AbsoluteAttribute call, the names
// must be in CEL's namespace resolution order. The name arguments provided here are
// returned in the same order as they were provided by the NamespacedAttribute
// CandidateVariableNames method.
AbsoluteAttribute(id int64, names ...string) NamespacedAttribute
// ConditionalAttribute creates an attribute with two Attribute branches, where the Attribute
// that is resolved depends on the boolean evaluation of the input 'expr'.
ConditionalAttribute(id int64, expr Interpretable, t, f Attribute) Attribute
// MaybeAttribute creates an attribute that refers to either a field selection or a namespaced
// variable name.
//
// Only expressions which have not been type-checked may generate oneof attributes.
MaybeAttribute(id int64, name string) Attribute
// RelativeAttribute creates an attribute whose value is a qualification of a dynamic
// computation rather than a static variable reference.
RelativeAttribute(id int64, operand Interpretable) Attribute
// NewQualifier creates a qualifier on the target object with a given value.
//
// The 'val' may be an Attribute or any proto-supported map key type: bool, int, string, uint.
//
// The qualifier may consider the object type being qualified, if present. If absent, the
// qualification should be considered dynamic and the qualification should still work, though
// it may be sub-optimal.
NewQualifier(objType *types.Type, qualID int64, val any, opt bool) (Qualifier, error)
}
// Qualifier marker interface for designating different qualifier values and where they appear
// within field selections and index call expressions (`_[_]`).
type Qualifier interface {
// ID where the qualifier appears within an expression.
ID() int64
// IsOptional specifies whether the qualifier is optional.
// Instead of a direct qualification, an optional qualifier will be resolved via QualifyIfPresent
// rather than Qualify. A non-optional qualifier may also be resolved through QualifyIfPresent if
// the object to qualify is itself optional.
IsOptional() bool
// Qualify performs a qualification, e.g. field selection, on the input object and returns
// the value of the access and whether the value was set. A non-nil value with a false presence
// test result indicates that the value being returned is the default value.
Qualify(vars Activation, obj any) (any, error)
// QualifyIfPresent qualifies the object if the qualifier is declared or defined on the object.
// The 'presenceOnly' flag indicates that the value is not necessary, just a boolean status as
// to whether the qualifier is present.
QualifyIfPresent(vars Activation, obj any, presenceOnly bool) (any, bool, error)
}
// ConstantQualifier interface embeds the Qualifier interface and provides an option to inspect the
// qualifier's constant value.
//
// Non-constant qualifiers are of Attribute type.
type ConstantQualifier interface {
Qualifier
// Value returns the constant value associated with the qualifier.
Value() ref.Val
}
// Attribute values are a variable or value with an optional set of qualifiers, such as field, key,
// or index accesses.
type Attribute interface {
Qualifier
// AddQualifier adds a qualifier on the Attribute or error if the qualification is not a valid qualifier type.
AddQualifier(Qualifier) (Attribute, error)
// Resolve returns the value of the Attribute and whether it was present given an Activation.
// For objects which support safe traversal, the value may be non-nil and the presence flag be false.
//
// If an error is encountered during attribute resolution, it will be returned immediately.
// If the attribute cannot be resolved within the Activation, the result must be: `nil`, `error`
// with the error indicating which variable was missing.
Resolve(Activation) (any, error)
}
// NamespacedAttribute values are a variable within a namespace, and an optional set of qualifiers
// such as field, key, or index accesses.
type NamespacedAttribute interface {
Attribute
// CandidateVariableNames returns the possible namespaced variable names for this Attribute in
// the CEL namespace resolution order.
CandidateVariableNames() []string
// Qualifiers returns the list of qualifiers associated with the Attribute.
Qualifiers() []Qualifier
}
// AttrFactoryOption specifies a functional option for configuring an attribute factory.
type AttrFactoryOption func(*attrFactory) *attrFactory
// EnableErrorOnBadPresenceTest error generation when a presence test or optional field selection
// is performed on a primitive type.
func EnableErrorOnBadPresenceTest(value bool) AttrFactoryOption {
return func(fac *attrFactory) *attrFactory {
fac.errorOnBadPresenceTest = value
return fac
}
}
// NewAttributeFactory returns a default AttributeFactory which is produces Attribute values
// capable of resolving types by simple names and qualify the values using the supported qualifier
// types: bool, int, string, and uint.
func NewAttributeFactory(cont *containers.Container, a types.Adapter, p types.Provider, opts ...AttrFactoryOption) AttributeFactory {
fac := &attrFactory{
container: cont,
adapter: a,
provider: p,
}
for _, o := range opts {
fac = o(fac)
}
return fac
}
type attrFactory struct {
container *containers.Container
adapter types.Adapter
provider types.Provider
errorOnBadPresenceTest bool
}
// AbsoluteAttribute refers to a variable value and an optional qualifier path.
//
// The namespaceNames represent the names the variable could have based on namespace
// resolution rules.
func (r *attrFactory) AbsoluteAttribute(id int64, names ...string) NamespacedAttribute {
return &absoluteAttribute{
id: id,
namespaceNames: names,
qualifiers: []Qualifier{},
adapter: r.adapter,
provider: r.provider,
fac: r,
errorOnBadPresenceTest: r.errorOnBadPresenceTest,
}
}
// ConditionalAttribute supports the case where an attribute selection may occur on a conditional
// expression, e.g. (cond ? a : b).c
func (r *attrFactory) ConditionalAttribute(id int64, expr Interpretable, t, f Attribute) Attribute {
return &conditionalAttribute{
id: id,
expr: expr,
truthy: t,
falsy: f,
adapter: r.adapter,
fac: r,
}
}
// MaybeAttribute collects variants of unchecked AbsoluteAttribute values which could either be
// direct variable accesses or some combination of variable access with qualification.
func (r *attrFactory) MaybeAttribute(id int64, name string) Attribute {
return &maybeAttribute{
id: id,
attrs: []NamespacedAttribute{
r.AbsoluteAttribute(id, r.container.ResolveCandidateNames(name)...),
},
adapter: r.adapter,
provider: r.provider,
fac: r,
}
}
// RelativeAttribute refers to an expression and an optional qualifier path.
func (r *attrFactory) RelativeAttribute(id int64, operand Interpretable) Attribute {
return &relativeAttribute{
id: id,
operand: operand,
qualifiers: []Qualifier{},
adapter: r.adapter,
fac: r,
errorOnBadPresenceTest: r.errorOnBadPresenceTest,
}
}
// NewQualifier is an implementation of the AttributeFactory interface.
func (r *attrFactory) NewQualifier(objType *types.Type, qualID int64, val any, opt bool) (Qualifier, error) {
// Before creating a new qualifier check to see if this is a protobuf message field access.
// If so, use the precomputed GetFrom qualification method rather than the standard
// stringQualifier.
str, isStr := val.(string)
if isStr && objType != nil && objType.Kind() == types.StructKind {
ft, found := r.provider.FindStructFieldType(objType.TypeName(), str)
if found && ft.IsSet != nil && ft.GetFrom != nil {
return &fieldQualifier{
id: qualID,
Name: str,
FieldType: ft,
adapter: r.adapter,
optional: opt,
}, nil
}
}
return newQualifier(r.adapter, qualID, val, opt, r.errorOnBadPresenceTest)
}
type absoluteAttribute struct {
id int64
// namespaceNames represent the names the variable could have based on declared container
// (package) of the expression.
namespaceNames []string
qualifiers []Qualifier
adapter types.Adapter
provider types.Provider
fac AttributeFactory
errorOnBadPresenceTest bool
}
// ID implements the Attribute interface method.
func (a *absoluteAttribute) ID() int64 {
qualCount := len(a.qualifiers)
if qualCount == 0 {
return a.id
}
return a.qualifiers[qualCount-1].ID()
}
// IsOptional returns trivially false for an attribute as the attribute represents a fully
// qualified variable name. If the attribute is used in an optional manner, then an attrQualifier
// is created and marks the attribute as optional.
func (a *absoluteAttribute) IsOptional() bool {
return false
}
// AddQualifier implements the Attribute interface method.
func (a *absoluteAttribute) AddQualifier(qual Qualifier) (Attribute, error) {
a.qualifiers = append(a.qualifiers, qual)
return a, nil
}
// CandidateVariableNames implements the NamespaceAttribute interface method.
func (a *absoluteAttribute) CandidateVariableNames() []string {
return a.namespaceNames
}
// Qualifiers returns the list of Qualifier instances associated with the namespaced attribute.
func (a *absoluteAttribute) Qualifiers() []Qualifier {
return a.qualifiers
}
// Qualify is an implementation of the Qualifier interface method.
func (a *absoluteAttribute) Qualify(vars Activation, obj any) (any, error) {
return attrQualify(a.fac, vars, obj, a)
}
// QualifyIfPresent is an implementation of the Qualifier interface method.
func (a *absoluteAttribute) QualifyIfPresent(vars Activation, obj any, presenceOnly bool) (any, bool, error) {
return attrQualifyIfPresent(a.fac, vars, obj, a, presenceOnly)
}
// String implements the Stringer interface method.
func (a *absoluteAttribute) String() string {
return fmt.Sprintf("id: %v, names: %v", a.id, a.namespaceNames)
}
// Resolve returns the resolved Attribute value given the Activation, or error if the Attribute
// variable is not found, or if its Qualifiers cannot be applied successfully.
//
// If the variable name cannot be found as an Activation variable or in the TypeProvider as
// a type, then the result is `nil`, `error` with the error indicating the name of the first
// variable searched as missing.
func (a *absoluteAttribute) Resolve(vars Activation) (any, error) {
for _, nm := range a.namespaceNames {
// If the variable is found, process it. Otherwise, wait until the checks to
// determine whether the type is unknown before returning.
obj, found := vars.ResolveName(nm)
if found {
if celErr, ok := obj.(*types.Err); ok {
return nil, celErr.Unwrap()
}
obj, isOpt, err := applyQualifiers(vars, obj, a.qualifiers)
if err != nil {
return nil, err
}
if isOpt {
val := a.adapter.NativeToValue(obj)
if types.IsUnknown(val) {
return val, nil
}
return types.OptionalOf(val), nil
}
return obj, nil
}
// Attempt to resolve the qualified type name if the name is not a variable identifier.
typ, found := a.provider.FindIdent(nm)
if found {
if len(a.qualifiers) == 0 {
return typ, nil
}
}
}
var attrNames strings.Builder
for i, nm := range a.namespaceNames {
if i != 0 {
attrNames.WriteString(", ")
}
attrNames.WriteString(nm)
}
return nil, missingAttribute(attrNames.String())
}
type conditionalAttribute struct {
id int64
expr Interpretable
truthy Attribute
falsy Attribute
adapter types.Adapter
fac AttributeFactory
}
// ID is an implementation of the Attribute interface method.
func (a *conditionalAttribute) ID() int64 {
// There's a field access after the conditional.
if a.truthy.ID() == a.falsy.ID() {
return a.truthy.ID()
}
// Otherwise return the conditional id as the consistent id being tracked.
return a.id
}
// IsOptional returns trivially false for an attribute as the attribute represents a fully
// qualified variable name. If the attribute is used in an optional manner, then an attrQualifier
// is created and marks the attribute as optional.
func (a *conditionalAttribute) IsOptional() bool {
return false
}
// AddQualifier appends the same qualifier to both sides of the conditional, in effect managing
// the qualification of alternate attributes.
func (a *conditionalAttribute) AddQualifier(qual Qualifier) (Attribute, error) {
_, err := a.truthy.AddQualifier(qual)
if err != nil {
return nil, err
}
_, err = a.falsy.AddQualifier(qual)
if err != nil {
return nil, err
}
return a, nil
}
// Qualify is an implementation of the Qualifier interface method.
func (a *conditionalAttribute) Qualify(vars Activation, obj any) (any, error) {
return attrQualify(a.fac, vars, obj, a)
}
// QualifyIfPresent is an implementation of the Qualifier interface method.
func (a *conditionalAttribute) QualifyIfPresent(vars Activation, obj any, presenceOnly bool) (any, bool, error) {
return attrQualifyIfPresent(a.fac, vars, obj, a, presenceOnly)
}
// Resolve evaluates the condition, and then resolves the truthy or falsy branch accordingly.
func (a *conditionalAttribute) Resolve(vars Activation) (any, error) {
val := a.expr.Eval(vars)
if val == types.True {
return a.truthy.Resolve(vars)
}
if val == types.False {
return a.falsy.Resolve(vars)
}
if types.IsUnknown(val) {
return val, nil
}
return nil, types.MaybeNoSuchOverloadErr(val).(*types.Err)
}
// String is an implementation of the Stringer interface method.
func (a *conditionalAttribute) String() string {
return fmt.Sprintf("id: %v, truthy attribute: %v, falsy attribute: %v", a.id, a.truthy, a.falsy)
}
type maybeAttribute struct {
id int64
attrs []NamespacedAttribute
adapter types.Adapter
provider types.Provider
fac AttributeFactory
}
// ID is an implementation of the Attribute interface method.
func (a *maybeAttribute) ID() int64 {
return a.attrs[0].ID()
}
// IsOptional returns trivially false for an attribute as the attribute represents a fully
// qualified variable name. If the attribute is used in an optional manner, then an attrQualifier
// is created and marks the attribute as optional.
func (a *maybeAttribute) IsOptional() bool {
return false
}
// AddQualifier adds a qualifier to each possible attribute variant, and also creates
// a new namespaced variable from the qualified value.
//
// The algorithm for building the maybe attribute is as follows:
//
// 1. Create a maybe attribute from a simple identifier when it occurs in a parsed-only expression
//
// mb = MaybeAttribute(<id>, "a")
//
// Initializing the maybe attribute creates an absolute attribute internally which includes the
// possible namespaced names of the attribute. In this example, let's assume we are in namespace
// 'ns', then the maybe is either one of the following variable names:
//
// possible variables names -- ns.a, a
//
// 2. Adding a qualifier to the maybe means that the variable name could be a longer qualified
// name, or a field selection on one of the possible variable names produced earlier:
//
// mb.AddQualifier("b")
//
// possible variables names -- ns.a.b, a.b
// possible field selection -- ns.a['b'], a['b']
//
// If none of the attributes within the maybe resolves a value, the result is an error.
func (a *maybeAttribute) AddQualifier(qual Qualifier) (Attribute, error) {
str := ""
isStr := false
cq, isConst := qual.(ConstantQualifier)
if isConst {
str, isStr = cq.Value().Value().(string)
}
var augmentedNames []string
// First add the qualifier to all existing attributes in the oneof.
for _, attr := range a.attrs {
if isStr && len(attr.Qualifiers()) == 0 {
candidateVars := attr.CandidateVariableNames()
augmentedNames = make([]string, len(candidateVars))
for i, name := range candidateVars {
augmentedNames[i] = fmt.Sprintf("%s.%s", name, str)
}
}
_, err := attr.AddQualifier(qual)
if err != nil {
return nil, err
}
}
// Next, ensure the most specific variable / type reference is searched first.
if len(augmentedNames) != 0 {
a.attrs = append([]NamespacedAttribute{a.fac.AbsoluteAttribute(qual.ID(), augmentedNames...)}, a.attrs...)
}
return a, nil
}
// Qualify is an implementation of the Qualifier interface method.
func (a *maybeAttribute) Qualify(vars Activation, obj any) (any, error) {
return attrQualify(a.fac, vars, obj, a)
}
// QualifyIfPresent is an implementation of the Qualifier interface method.
func (a *maybeAttribute) QualifyIfPresent(vars Activation, obj any, presenceOnly bool) (any, bool, error) {
return attrQualifyIfPresent(a.fac, vars, obj, a, presenceOnly)
}
// Resolve follows the variable resolution rules to determine whether the attribute is a variable
// or a field selection.
func (a *maybeAttribute) Resolve(vars Activation) (any, error) {
var maybeErr error
for _, attr := range a.attrs {
obj, err := attr.Resolve(vars)
// Return an error if one is encountered.
if err != nil {
resErr, ok := err.(*resolutionError)
if !ok {
return nil, err
}
// If this was not a missing variable error, return it.
if !resErr.isMissingAttribute() {
return nil, err
}
// When the variable is missing in a maybe attribute we defer erroring.
if maybeErr == nil {
maybeErr = resErr
}
// Continue attempting to resolve possible variables.
continue
}
return obj, nil
}
// Else, produce a no such attribute error.
return nil, maybeErr
}
// String is an implementation of the Stringer interface method.
func (a *maybeAttribute) String() string {
return fmt.Sprintf("id: %v, attributes: %v", a.id, a.attrs)
}
type relativeAttribute struct {
id int64
operand Interpretable
qualifiers []Qualifier
adapter types.Adapter
fac AttributeFactory
errorOnBadPresenceTest bool
}
// ID is an implementation of the Attribute interface method.
func (a *relativeAttribute) ID() int64 {
qualCount := len(a.qualifiers)
if qualCount == 0 {
return a.id
}
return a.qualifiers[qualCount-1].ID()
}
// IsOptional returns trivially false for an attribute as the attribute represents a fully
// qualified variable name. If the attribute is used in an optional manner, then an attrQualifier
// is created and marks the attribute as optional.
func (a *relativeAttribute) IsOptional() bool {
return false
}
// AddQualifier implements the Attribute interface method.
func (a *relativeAttribute) AddQualifier(qual Qualifier) (Attribute, error) {
a.qualifiers = append(a.qualifiers, qual)
return a, nil
}
// Qualify is an implementation of the Qualifier interface method.
func (a *relativeAttribute) Qualify(vars Activation, obj any) (any, error) {
return attrQualify(a.fac, vars, obj, a)
}
// QualifyIfPresent is an implementation of the Qualifier interface method.
func (a *relativeAttribute) QualifyIfPresent(vars Activation, obj any, presenceOnly bool) (any, bool, error) {
return attrQualifyIfPresent(a.fac, vars, obj, a, presenceOnly)
}
// Resolve expression value and qualifier relative to the expression result.
func (a *relativeAttribute) Resolve(vars Activation) (any, error) {
// First, evaluate the operand.
v := a.operand.Eval(vars)
if types.IsError(v) {
return nil, v.(*types.Err)
}
if types.IsUnknown(v) {
return v, nil
}
obj, isOpt, err := applyQualifiers(vars, v, a.qualifiers)
if err != nil {
return nil, err
}
if isOpt {
val := a.adapter.NativeToValue(obj)
if types.IsUnknown(val) {
return val, nil
}
return types.OptionalOf(val), nil
}
return obj, nil
}
// String is an implementation of the Stringer interface method.
func (a *relativeAttribute) String() string {
return fmt.Sprintf("id: %v, operand: %v", a.id, a.operand)
}
func newQualifier(adapter types.Adapter, id int64, v any, opt, errorOnBadPresenceTest bool) (Qualifier, error) {
var qual Qualifier
switch val := v.(type) {
case Attribute:
// Note, attributes are initially identified as non-optional since they represent a top-level
// field access; however, when used as a relative qualifier, e.g. a[?b.c], then an attrQualifier
// is created which intercepts the IsOptional check for the attribute in order to return the
// correct result.
return &attrQualifier{
id: id,
Attribute: val,
optional: opt,
}, nil
case string:
qual = &stringQualifier{
id: id,
value: val,
celValue: types.String(val),
adapter: adapter,
optional: opt,
errorOnBadPresenceTest: errorOnBadPresenceTest,
}
case int:
qual = &intQualifier{
id: id,
value: int64(val),
celValue: types.Int(val),
adapter: adapter,
optional: opt,
errorOnBadPresenceTest: errorOnBadPresenceTest,
}
case int32:
qual = &intQualifier{
id: id,
value: int64(val),
celValue: types.Int(val),
adapter: adapter,
optional: opt,
errorOnBadPresenceTest: errorOnBadPresenceTest,
}
case int64:
qual = &intQualifier{
id: id,
value: val,
celValue: types.Int(val),
adapter: adapter,
optional: opt,
errorOnBadPresenceTest: errorOnBadPresenceTest,
}
case uint:
qual = &uintQualifier{
id: id,
value: uint64(val),
celValue: types.Uint(val),
adapter: adapter,
optional: opt,
errorOnBadPresenceTest: errorOnBadPresenceTest,
}
case uint32:
qual = &uintQualifier{
id: id,
value: uint64(val),
celValue: types.Uint(val),
adapter: adapter,
optional: opt,
errorOnBadPresenceTest: errorOnBadPresenceTest,
}
case uint64:
qual = &uintQualifier{
id: id,
value: val,
celValue: types.Uint(val),
adapter: adapter,
optional: opt,
errorOnBadPresenceTest: errorOnBadPresenceTest,
}
case bool:
qual = &boolQualifier{
id: id,
value: val,
celValue: types.Bool(val),
adapter: adapter,
optional: opt,
errorOnBadPresenceTest: errorOnBadPresenceTest,
}
case float32:
qual = &doubleQualifier{
id: id,
value: float64(val),
celValue: types.Double(val),
adapter: adapter,
optional: opt,
errorOnBadPresenceTest: errorOnBadPresenceTest,
}
case float64:
qual = &doubleQualifier{
id: id,
value: val,
celValue: types.Double(val),
adapter: adapter,
optional: opt,
errorOnBadPresenceTest: errorOnBadPresenceTest,
}
case types.String:
qual = &stringQualifier{
id: id,
value: string(val),
celValue: val,
adapter: adapter,
optional: opt,
errorOnBadPresenceTest: errorOnBadPresenceTest,
}
case types.Int:
qual = &intQualifier{
id: id,
value: int64(val),
celValue: val,
adapter: adapter,
optional: opt,
errorOnBadPresenceTest: errorOnBadPresenceTest,
}
case types.Uint:
qual = &uintQualifier{
id: id,
value: uint64(val),
celValue: val,
adapter: adapter,
optional: opt,
errorOnBadPresenceTest: errorOnBadPresenceTest,
}
case types.Bool:
qual = &boolQualifier{
id: id,
value: bool(val),
celValue: val,
adapter: adapter,
optional: opt,
errorOnBadPresenceTest: errorOnBadPresenceTest,
}
case types.Double:
qual = &doubleQualifier{
id: id,
value: float64(val),
celValue: val,
adapter: adapter,
optional: opt,
errorOnBadPresenceTest: errorOnBadPresenceTest,
}
case *types.Unknown:
qual = &unknownQualifier{id: id, value: val}
default:
if q, ok := v.(Qualifier); ok {
return q, nil
}
return nil, fmt.Errorf("invalid qualifier type: %T", v)
}
return qual, nil
}
type attrQualifier struct {
id int64
Attribute
optional bool
}
// ID implements the Qualifier interface method and returns the qualification instruction id
// rather than the attribute id.
func (q *attrQualifier) ID() int64 {
return q.id
}
// IsOptional implements the Qualifier interface method.
func (q *attrQualifier) IsOptional() bool {
return q.optional
}
type stringQualifier struct {
id int64
value string
celValue ref.Val
adapter types.Adapter
optional bool
errorOnBadPresenceTest bool
}
// ID is an implementation of the Qualifier interface method.
func (q *stringQualifier) ID() int64 {
return q.id
}
// IsOptional implements the Qualifier interface method.
func (q *stringQualifier) IsOptional() bool {
return q.optional
}
// Qualify implements the Qualifier interface method.
func (q *stringQualifier) Qualify(vars Activation, obj any) (any, error) {
val, _, err := q.qualifyInternal(vars, obj, false, false)
return val, err
}
// QualifyIfPresent is an implementation of the Qualifier interface method.
func (q *stringQualifier) QualifyIfPresent(vars Activation, obj any, presenceOnly bool) (any, bool, error) {
return q.qualifyInternal(vars, obj, true, presenceOnly)
}
func (q *stringQualifier) qualifyInternal(vars Activation, obj any, presenceTest, presenceOnly bool) (any, bool, error) {
s := q.value
switch o := obj.(type) {
case map[string]any:
obj, isKey := o[s]
if isKey {
return obj, true, nil
}
case map[string]string:
obj, isKey := o[s]
if isKey {
return obj, true, nil
}
case map[string]int:
obj, isKey := o[s]
if isKey {
return obj, true, nil
}
case map[string]int32:
obj, isKey := o[s]
if isKey {
return obj, true, nil
}
case map[string]int64:
obj, isKey := o[s]
if isKey {
return obj, true, nil
}
case map[string]uint:
obj, isKey := o[s]
if isKey {
return obj, true, nil
}
case map[string]uint32:
obj, isKey := o[s]
if isKey {
return obj, true, nil
}
case map[string]uint64:
obj, isKey := o[s]
if isKey {
return obj, true, nil
}
case map[string]float32:
obj, isKey := o[s]
if isKey {
return obj, true, nil
}
case map[string]float64:
obj, isKey := o[s]
if isKey {
return obj, true, nil
}
case map[string]bool:
obj, isKey := o[s]
if isKey {
return obj, true, nil
}
default:
return refQualify(q.adapter, obj, q.celValue, presenceTest, presenceOnly, q.errorOnBadPresenceTest)
}
if presenceTest {
return nil, false, nil
}
return nil, false, missingKey(q.celValue)
}
// Value implements the ConstantQualifier interface
func (q *stringQualifier) Value() ref.Val {
return q.celValue
}
type intQualifier struct {
id int64
value int64
celValue ref.Val
adapter types.Adapter
optional bool
errorOnBadPresenceTest bool
}
// ID is an implementation of the Qualifier interface method.
func (q *intQualifier) ID() int64 {
return q.id
}
// IsOptional implements the Qualifier interface method.
func (q *intQualifier) IsOptional() bool {
return q.optional
}
// Qualify implements the Qualifier interface method.
func (q *intQualifier) Qualify(vars Activation, obj any) (any, error) {
val, _, err := q.qualifyInternal(vars, obj, false, false)
return val, err
}
// QualifyIfPresent is an implementation of the Qualifier interface method.
func (q *intQualifier) QualifyIfPresent(vars Activation, obj any, presenceOnly bool) (any, bool, error) {
return q.qualifyInternal(vars, obj, true, presenceOnly)
}
func (q *intQualifier) qualifyInternal(vars Activation, obj any, presenceTest, presenceOnly bool) (any, bool, error) {
i := q.value
var isMap bool
switch o := obj.(type) {
// The specialized map types supported by an int qualifier are considerably fewer than the set
// of specialized map types supported by string qualifiers since they are less frequently used
// than string-based map keys. Additional specializations may be added in the future if
// desired.
case map[int]any:
isMap = true
obj, isKey := o[int(i)]
if isKey {
return obj, true, nil
}
case map[int32]any:
isMap = true
obj, isKey := o[int32(i)]
if isKey {
return obj, true, nil
}
case map[int64]any:
isMap = true
obj, isKey := o[i]
if isKey {
return obj, true, nil
}
case []any:
isIndex := i >= 0 && i < int64(len(o))
if isIndex {
return o[i], true, nil
}
case []string:
isIndex := i >= 0 && i < int64(len(o))
if isIndex {
return o[i], true, nil
}
case []int:
isIndex := i >= 0 && i < int64(len(o))
if isIndex {
return o[i], true, nil
}
case []int32:
isIndex := i >= 0 && i < int64(len(o))
if isIndex {
return o[i], true, nil
}
case []int64:
isIndex := i >= 0 && i < int64(len(o))
if isIndex {
return o[i], true, nil
}
case []uint:
isIndex := i >= 0 && i < int64(len(o))
if isIndex {
return o[i], true, nil
}
case []uint32:
isIndex := i >= 0 && i < int64(len(o))
if isIndex {
return o[i], true, nil
}
case []uint64:
isIndex := i >= 0 && i < int64(len(o))
if isIndex {
return o[i], true, nil
}
case []float32:
isIndex := i >= 0 && i < int64(len(o))
if isIndex {
return o[i], true, nil
}
case []float64:
isIndex := i >= 0 && i < int64(len(o))
if isIndex {
return o[i], true, nil
}
case []bool:
isIndex := i >= 0 && i < int64(len(o))
if isIndex {
return o[i], true, nil
}
default:
return refQualify(q.adapter, obj, q.celValue, presenceTest, presenceOnly, q.errorOnBadPresenceTest)
}
if presenceTest {
return nil, false, nil
}
if isMap {
return nil, false, missingKey(q.celValue)
}
return nil, false, missingIndex(q.celValue)
}
// Value implements the ConstantQualifier interface
func (q *intQualifier) Value() ref.Val {
return q.celValue
}
type uintQualifier struct {
id int64
value uint64
celValue ref.Val
adapter types.Adapter
optional bool
errorOnBadPresenceTest bool
}
// ID is an implementation of the Qualifier interface method.
func (q *uintQualifier) ID() int64 {
return q.id
}
// IsOptional implements the Qualifier interface method.
func (q *uintQualifier) IsOptional() bool {
return q.optional
}
// Qualify implements the Qualifier interface method.
func (q *uintQualifier) Qualify(vars Activation, obj any) (any, error) {
val, _, err := q.qualifyInternal(vars, obj, false, false)
return val, err
}
// QualifyIfPresent is an implementation of the Qualifier interface method.
func (q *uintQualifier) QualifyIfPresent(vars Activation, obj any, presenceOnly bool) (any, bool, error) {
return q.qualifyInternal(vars, obj, true, presenceOnly)
}
func (q *uintQualifier) qualifyInternal(vars Activation, obj any, presenceTest, presenceOnly bool) (any, bool, error) {
u := q.value
switch o := obj.(type) {
// The specialized map types supported by a uint qualifier are considerably fewer than the set
// of specialized map types supported by string qualifiers since they are less frequently used
// than string-based map keys. Additional specializations may be added in the future if
// desired.
case map[uint]any:
obj, isKey := o[uint(u)]
if isKey {
return obj, true, nil
}
case map[uint32]any:
obj, isKey := o[uint32(u)]
if isKey {
return obj, true, nil
}
case map[uint64]any:
obj, isKey := o[u]
if isKey {
return obj, true, nil
}
default:
return refQualify(q.adapter, obj, q.celValue, presenceTest, presenceOnly, q.errorOnBadPresenceTest)
}
if presenceTest {
return nil, false, nil
}
return nil, false, missingKey(q.celValue)
}
// Value implements the ConstantQualifier interface
func (q *uintQualifier) Value() ref.Val {
return q.celValue
}
type boolQualifier struct {
id int64
value bool
celValue ref.Val
adapter types.Adapter
optional bool
errorOnBadPresenceTest bool
}
// ID is an implementation of the Qualifier interface method.
func (q *boolQualifier) ID() int64 {
return q.id
}
// IsOptional implements the Qualifier interface method.
func (q *boolQualifier) IsOptional() bool {
return q.optional
}
// Qualify implements the Qualifier interface method.
func (q *boolQualifier) Qualify(vars Activation, obj any) (any, error) {
val, _, err := q.qualifyInternal(vars, obj, false, false)
return val, err
}
// QualifyIfPresent is an implementation of the Qualifier interface method.
func (q *boolQualifier) QualifyIfPresent(vars Activation, obj any, presenceOnly bool) (any, bool, error) {
return q.qualifyInternal(vars, obj, true, presenceOnly)
}
func (q *boolQualifier) qualifyInternal(vars Activation, obj any, presenceTest, presenceOnly bool) (any, bool, error) {
b := q.value
switch o := obj.(type) {
case map[bool]any:
obj, isKey := o[b]
if isKey {
return obj, true, nil
}
default:
return refQualify(q.adapter, obj, q.celValue, presenceTest, presenceOnly, q.errorOnBadPresenceTest)
}
if presenceTest {
return nil, false, nil
}
return nil, false, missingKey(q.celValue)
}
// Value implements the ConstantQualifier interface
func (q *boolQualifier) Value() ref.Val {
return q.celValue
}
// fieldQualifier indicates that the qualification is a well-defined field with a known
// field type. When the field type is known this can be used to improve the speed and
// efficiency of field resolution.
type fieldQualifier struct {
id int64
Name string
FieldType *types.FieldType
adapter types.Adapter
optional bool
}
// ID is an implementation of the Qualifier interface method.
func (q *fieldQualifier) ID() int64 {
return q.id
}
// IsOptional implements the Qualifier interface method.
func (q *fieldQualifier) IsOptional() bool {
return q.optional
}
// Qualify implements the Qualifier interface method.
func (q *fieldQualifier) Qualify(vars Activation, obj any) (any, error) {
if rv, ok := obj.(ref.Val); ok {
obj = rv.Value()
}
val, err := q.FieldType.GetFrom(obj)
if err != nil {
return nil, err
}
return val, nil
}
// QualifyIfPresent is an implementation of the Qualifier interface method.
func (q *fieldQualifier) QualifyIfPresent(vars Activation, obj any, presenceOnly bool) (any, bool, error) {
if rv, ok := obj.(ref.Val); ok {
obj = rv.Value()
}
if !q.FieldType.IsSet(obj) {
return nil, false, nil
}
if presenceOnly {
return nil, true, nil
}
val, err := q.FieldType.GetFrom(obj)
if err != nil {
return nil, false, err
}
return val, true, nil
}
// Value implements the ConstantQualifier interface
func (q *fieldQualifier) Value() ref.Val {
return types.String(q.Name)
}
// doubleQualifier qualifies a CEL object, map, or list using a double value.
//
// This qualifier is used for working with dynamic data like JSON or protobuf.Any where the value
// type may not be known ahead of time and may not conform to the standard types supported as valid
// protobuf map key types.
type doubleQualifier struct {
id int64
value float64
celValue ref.Val
adapter types.Adapter
optional bool
errorOnBadPresenceTest bool
}
// ID is an implementation of the Qualifier interface method.
func (q *doubleQualifier) ID() int64 {
return q.id
}
// IsOptional implements the Qualifier interface method.
func (q *doubleQualifier) IsOptional() bool {
return q.optional
}
// Qualify implements the Qualifier interface method.
func (q *doubleQualifier) Qualify(vars Activation, obj any) (any, error) {
val, _, err := q.qualifyInternal(vars, obj, false, false)
return val, err
}
func (q *doubleQualifier) QualifyIfPresent(vars Activation, obj any, presenceOnly bool) (any, bool, error) {
return q.qualifyInternal(vars, obj, true, presenceOnly)
}
func (q *doubleQualifier) qualifyInternal(vars Activation, obj any, presenceTest, presenceOnly bool) (any, bool, error) {
return refQualify(q.adapter, obj, q.celValue, presenceTest, presenceOnly, q.errorOnBadPresenceTest)
}
// Value implements the ConstantQualifier interface
func (q *doubleQualifier) Value() ref.Val {
return q.celValue
}
// unknownQualifier is a simple qualifier which always returns a preconfigured set of unknown values
// for any value subject to qualification. This is consistent with CEL's unknown handling elsewhere.
type unknownQualifier struct {
id int64
value *types.Unknown
}
// ID is an implementation of the Qualifier interface method.
func (q *unknownQualifier) ID() int64 {
return q.id
}
// IsOptional returns trivially false as an the unknown value is always returned.
func (q *unknownQualifier) IsOptional() bool {
return false
}
// Qualify returns the unknown value associated with this qualifier.
func (q *unknownQualifier) Qualify(vars Activation, obj any) (any, error) {
return q.value, nil
}
// QualifyIfPresent is an implementation of the Qualifier interface method.
func (q *unknownQualifier) QualifyIfPresent(vars Activation, obj any, presenceOnly bool) (any, bool, error) {
return q.value, true, nil
}
// Value implements the ConstantQualifier interface
func (q *unknownQualifier) Value() ref.Val {
return q.value
}
func applyQualifiers(vars Activation, obj any, qualifiers []Qualifier) (any, bool, error) {
optObj, isOpt := obj.(*types.Optional)
if isOpt {
if !optObj.HasValue() {
return optObj, false, nil
}
obj = optObj.GetValue().Value()
}
var err error
for _, qual := range qualifiers {
var qualObj any
isOpt = isOpt || qual.IsOptional()
if isOpt {
var present bool
qualObj, present, err = qual.QualifyIfPresent(vars, obj, false)
if err != nil {
return nil, false, err
}
if !present {
// We return optional none here with a presence of 'false' as the layers
// above will attempt to call types.OptionalOf() on a present value if any
// of the qualifiers is optional.
return types.OptionalNone, false, nil
}
} else {
qualObj, err = qual.Qualify(vars, obj)
if err != nil {
return nil, false, err
}
}
obj = qualObj
}
return obj, isOpt, nil
}
// attrQualify performs a qualification using the result of an attribute evaluation.
func attrQualify(fac AttributeFactory, vars Activation, obj any, qualAttr Attribute) (any, error) {
val, err := qualAttr.Resolve(vars)
if err != nil {
return nil, err
}
qual, err := fac.NewQualifier(nil, qualAttr.ID(), val, qualAttr.IsOptional())
if err != nil {
return nil, err
}
return qual.Qualify(vars, obj)
}
// attrQualifyIfPresent conditionally performs the qualification of the result of attribute is present
// on the target object.
func attrQualifyIfPresent(fac AttributeFactory, vars Activation, obj any, qualAttr Attribute,
presenceOnly bool) (any, bool, error) {
val, err := qualAttr.Resolve(vars)
if err != nil {
return nil, false, err
}
qual, err := fac.NewQualifier(nil, qualAttr.ID(), val, qualAttr.IsOptional())
if err != nil {
return nil, false, err
}
return qual.QualifyIfPresent(vars, obj, presenceOnly)
}
// refQualify attempts to convert the value to a CEL value and then uses reflection methods to try and
// apply the qualifier with the option to presence test field accesses before retrieving field values.
func refQualify(adapter types.Adapter, obj any, idx ref.Val, presenceTest, presenceOnly, errorOnBadPresenceTest bool) (ref.Val, bool, error) {
celVal := adapter.NativeToValue(obj)
switch v := celVal.(type) {
case *types.Unknown:
return v, true, nil
case *types.Err:
return nil, false, v
case traits.Mapper:
val, found := v.Find(idx)
// If the index is of the wrong type for the map, then it is possible
// for the Find call to produce an error.
if types.IsError(val) {
return nil, false, val.(*types.Err)
}
if found {
return val, true, nil
}
if presenceTest {
return nil, false, nil
}
return nil, false, missingKey(idx)
case traits.Lister:
// If the index argument is not a valid numeric type, then it is possible
// for the index operation to produce an error.
i, err := types.IndexOrError(idx)
if err != nil {
return nil, false, err
}
celIndex := types.Int(i)
if i >= 0 && celIndex < v.Size().(types.Int) {
return v.Get(idx), true, nil
}
if presenceTest {
return nil, false, nil
}
return nil, false, missingIndex(idx)
case traits.Indexer:
if presenceTest {
ft, ok := v.(traits.FieldTester)
if ok {
presence := ft.IsSet(idx)
if types.IsError(presence) {
return nil, false, presence.(*types.Err)
}
// If not found or presence only test, then return.
// Otherwise, if found, obtain the value later on.
if presenceOnly || presence == types.False {
return nil, presence == types.True, nil
}
}
}
val := v.Get(idx)
if types.IsError(val) {
return nil, false, val.(*types.Err)
}
return val, true, nil
default:
if presenceTest && !errorOnBadPresenceTest {
return nil, false, nil
}
return nil, false, missingKey(idx)
}
}
// resolutionError is a custom error type which encodes the different error states which may
// occur during attribute resolution.
type resolutionError struct {
missingAttribute string
missingIndex ref.Val
missingKey ref.Val
}
func (e *resolutionError) isMissingAttribute() bool {
return e.missingAttribute != ""
}
func missingIndex(missing ref.Val) *resolutionError {
return &resolutionError{
missingIndex: missing,
}
}
func missingKey(missing ref.Val) *resolutionError {
return &resolutionError{
missingKey: missing,
}
}
func missingAttribute(attr string) *resolutionError {
return &resolutionError{
missingAttribute: attr,
}
}
// Error implements the error interface method.
func (e *resolutionError) Error() string {
if e.missingKey != nil {
return fmt.Sprintf("no such key: %v", e.missingKey)
}
if e.missingIndex != nil {
return fmt.Sprintf("index out of bounds: %v", e.missingIndex)
}
if e.missingAttribute != "" {
return fmt.Sprintf("no such attribute(s): %s", e.missingAttribute)
}
return "invalid attribute"
}
// Is implements the errors.Is() method used by more recent versions of Go.
func (e *resolutionError) Is(err error) bool {
return err.Error() == e.Error()
}
// Copyright 2018 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package interpreter
import (
"github.com/google/cel-go/common/overloads"
"github.com/google/cel-go/common/types"
"github.com/google/cel-go/common/types/ref"
"github.com/google/cel-go/common/types/traits"
)
// InterpretableDecorator is a functional interface for decorating or replacing
// Interpretable expression nodes at construction time.
type InterpretableDecorator func(Interpretable) (Interpretable, error)
// decObserveEval records evaluation state into an EvalState object.
func decObserveEval(observer EvalObserver) InterpretableDecorator {
return func(i Interpretable) (Interpretable, error) {
switch inst := i.(type) {
case *evalWatch, *evalWatchAttr, *evalWatchConst, *evalWatchConstructor:
// these instruction are already watching, return straight-away.
return i, nil
case InterpretableAttribute:
return &evalWatchAttr{
InterpretableAttribute: inst,
observer: observer,
}, nil
case InterpretableConst:
return &evalWatchConst{
InterpretableConst: inst,
observer: observer,
}, nil
case InterpretableConstructor:
return &evalWatchConstructor{
constructor: inst,
observer: observer,
}, nil
default:
return &evalWatch{
Interpretable: i,
observer: observer,
}, nil
}
}
}
// decInterruptFolds creates an intepretable decorator which marks comprehensions as interruptable
// where the interrupt state is communicated via a hidden variable on the Activation.
func decInterruptFolds() InterpretableDecorator {
return func(i Interpretable) (Interpretable, error) {
fold, ok := i.(*evalFold)
if !ok {
return i, nil
}
fold.interruptable = true
return fold, nil
}
}
// decDisableShortcircuits ensures that all branches of an expression will be evaluated, no short-circuiting.
func decDisableShortcircuits() InterpretableDecorator {
return func(i Interpretable) (Interpretable, error) {
switch expr := i.(type) {
case *evalOr:
return &evalExhaustiveOr{
id: expr.id,
terms: expr.terms,
}, nil
case *evalAnd:
return &evalExhaustiveAnd{
id: expr.id,
terms: expr.terms,
}, nil
case *evalFold:
expr.exhaustive = true
return expr, nil
case InterpretableAttribute:
cond, isCond := expr.Attr().(*conditionalAttribute)
if isCond {
return &evalExhaustiveConditional{
id: cond.id,
attr: cond,
adapter: expr.Adapter(),
}, nil
}
}
return i, nil
}
}
// decOptimize optimizes the program plan by looking for common evaluation patterns and
// conditionally precomputing the result.
// - build list and map values with constant elements.
// - convert 'in' operations to set membership tests if possible.
func decOptimize() InterpretableDecorator {
return func(i Interpretable) (Interpretable, error) {
switch inst := i.(type) {
case *evalList:
return maybeBuildListLiteral(i, inst)
case *evalMap:
return maybeBuildMapLiteral(i, inst)
case InterpretableCall:
if inst.OverloadID() == overloads.InList {
return maybeOptimizeSetMembership(i, inst)
}
if overloads.IsTypeConversionFunction(inst.Function()) {
return maybeOptimizeConstUnary(i, inst)
}
}
return i, nil
}
}
// decRegexOptimizer compiles regex pattern string constants.
func decRegexOptimizer(regexOptimizations ...*RegexOptimization) InterpretableDecorator {
functionMatchMap := make(map[string]*RegexOptimization)
overloadMatchMap := make(map[string]*RegexOptimization)
for _, m := range regexOptimizations {
functionMatchMap[m.Function] = m
if m.OverloadID != "" {
overloadMatchMap[m.OverloadID] = m
}
}
return func(i Interpretable) (Interpretable, error) {
call, ok := i.(InterpretableCall)
if !ok {
return i, nil
}
var matcher *RegexOptimization
var found bool
if call.OverloadID() != "" {
matcher, found = overloadMatchMap[call.OverloadID()]
}
if !found {
matcher, found = functionMatchMap[call.Function()]
}
if !found || matcher.RegexIndex >= len(call.Args()) {
return i, nil
}
args := call.Args()
regexArg := args[matcher.RegexIndex]
regexStr, isConst := regexArg.(InterpretableConst)
if !isConst {
return i, nil
}
pattern, ok := regexStr.Value().(types.String)
if !ok {
return i, nil
}
return matcher.Factory(call, string(pattern))
}
}
func maybeOptimizeConstUnary(i Interpretable, call InterpretableCall) (Interpretable, error) {
args := call.Args()
if len(args) != 1 {
return i, nil
}
_, isConst := args[0].(InterpretableConst)
if !isConst {
return i, nil
}
val := call.Eval(EmptyActivation())
if types.IsError(val) {
return nil, val.(*types.Err)
}
return NewConstValue(call.ID(), val), nil
}
func maybeBuildListLiteral(i Interpretable, l *evalList) (Interpretable, error) {
for _, elem := range l.elems {
_, isConst := elem.(InterpretableConst)
if !isConst {
return i, nil
}
}
return NewConstValue(l.ID(), l.Eval(EmptyActivation())), nil
}
func maybeBuildMapLiteral(i Interpretable, mp *evalMap) (Interpretable, error) {
for idx, key := range mp.keys {
_, isConst := key.(InterpretableConst)
if !isConst {
return i, nil
}
_, isConst = mp.vals[idx].(InterpretableConst)
if !isConst {
return i, nil
}
}
return NewConstValue(mp.ID(), mp.Eval(EmptyActivation())), nil
}
// maybeOptimizeSetMembership may convert an 'in' operation against a list to map key membership
// test if the following conditions are true:
// - the list is a constant with homogeneous element types.
// - the elements are all of primitive type.
func maybeOptimizeSetMembership(i Interpretable, inlist InterpretableCall) (Interpretable, error) {
args := inlist.Args()
lhs := args[0]
rhs := args[1]
l, isConst := rhs.(InterpretableConst)
if !isConst {
return i, nil
}
// When the incoming binary call is flagged with as the InList overload, the value will
// always be convertible to a `traits.Lister` type.
list := l.Value().(traits.Lister)
if list.Size() == types.IntZero {
return NewConstValue(inlist.ID(), types.False), nil
}
it := list.Iterator()
valueSet := make(map[ref.Val]ref.Val)
for it.HasNext() == types.True {
elem := it.Next()
if !types.IsPrimitiveType(elem) || elem.Type() == types.BytesType {
// Note, non-primitive type are not yet supported, and []byte isn't hashable.
return i, nil
}
valueSet[elem] = types.True
switch ev := elem.(type) {
case types.Double:
iv := ev.ConvertToType(types.IntType)
// Ensure that only lossless conversions are added to the set
if !types.IsError(iv) && iv.Equal(ev) == types.True {
valueSet[iv] = types.True
}
// Ensure that only lossless conversions are added to the set
uv := ev.ConvertToType(types.UintType)
if !types.IsError(uv) && uv.Equal(ev) == types.True {
valueSet[uv] = types.True
}
case types.Int:
dv := ev.ConvertToType(types.DoubleType)
if !types.IsError(dv) {
valueSet[dv] = types.True
}
uv := ev.ConvertToType(types.UintType)
if !types.IsError(uv) {
valueSet[uv] = types.True
}
case types.Uint:
dv := ev.ConvertToType(types.DoubleType)
if !types.IsError(dv) {
valueSet[dv] = types.True
}
iv := ev.ConvertToType(types.IntType)
if !types.IsError(iv) {
valueSet[iv] = types.True
}
}
}
return &evalSetMembership{
inst: inlist,
arg: lhs,
valueSet: valueSet,
}, nil
}
// Copyright 2018 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package interpreter
import (
"fmt"
"github.com/google/cel-go/common/functions"
)
// Dispatcher resolves function calls to their appropriate overload.
type Dispatcher interface {
// Add one or more overloads, returning an error if any Overload has the same Overload#Name.
Add(overloads ...*functions.Overload) error
// FindOverload returns an Overload definition matching the provided name.
FindOverload(overload string) (*functions.Overload, bool)
// OverloadIds returns the set of all overload identifiers configured for dispatch.
OverloadIds() []string
}
// NewDispatcher returns an empty Dispatcher instance.
func NewDispatcher() Dispatcher {
return &defaultDispatcher{
overloads: make(map[string]*functions.Overload)}
}
// ExtendDispatcher returns a Dispatcher which inherits the overloads of its parent, and
// provides an isolation layer between built-ins and extension functions which is useful
// for forward compatibility.
func ExtendDispatcher(parent Dispatcher) Dispatcher {
return &defaultDispatcher{
parent: parent,
overloads: make(map[string]*functions.Overload)}
}
// overloadMap helper type for indexing overloads by function name.
type overloadMap map[string]*functions.Overload
// defaultDispatcher struct which contains an overload map.
type defaultDispatcher struct {
parent Dispatcher
overloads overloadMap
}
// Add implements the Dispatcher.Add interface method.
func (d *defaultDispatcher) Add(overloads ...*functions.Overload) error {
for _, o := range overloads {
// add the overload unless an overload of the same name has already been provided.
if _, found := d.overloads[o.Operator]; found {
return fmt.Errorf("overload already exists '%s'", o.Operator)
}
// index the overload by function name.
d.overloads[o.Operator] = o
}
return nil
}
// FindOverload implements the Dispatcher.FindOverload interface method.
func (d *defaultDispatcher) FindOverload(overload string) (*functions.Overload, bool) {
o, found := d.overloads[overload]
// Attempt to dispatch to an overload defined in the parent.
if !found && d.parent != nil {
return d.parent.FindOverload(overload)
}
return o, found
}
// OverloadIds implements the Dispatcher interface method.
func (d *defaultDispatcher) OverloadIds() []string {
i := 0
overloads := make([]string, len(d.overloads))
for name := range d.overloads {
overloads[i] = name
i++
}
if d.parent == nil {
return overloads
}
parentOverloads := d.parent.OverloadIds()
for _, pName := range parentOverloads {
if _, found := d.overloads[pName]; !found {
overloads = append(overloads, pName)
}
}
return overloads
}
// Copyright 2018 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package interpreter
import (
"github.com/google/cel-go/common/types/ref"
)
// EvalState tracks the values associated with expression ids during execution.
type EvalState interface {
// IDs returns the list of ids with recorded values.
IDs() []int64
// Value returns the observed value of the given expression id if found, and a nil false
// result if not.
Value(int64) (ref.Val, bool)
// SetValue sets the observed value of the expression id.
SetValue(int64, ref.Val)
// Reset clears the previously recorded expression values.
Reset()
}
// evalState permits the mutation of evaluation state for a given expression id.
type evalState struct {
values map[int64]ref.Val
}
// NewEvalState returns an EvalState instanced used to observe the intermediate
// evaluations of an expression.
func NewEvalState() EvalState {
return &evalState{
values: make(map[int64]ref.Val),
}
}
// IDs implements the EvalState interface method.
func (s *evalState) IDs() []int64 {
var ids []int64
for k, v := range s.values {
if v != nil {
ids = append(ids, k)
}
}
return ids
}
// Value is an implementation of the EvalState interface method.
func (s *evalState) Value(exprID int64) (ref.Val, bool) {
val, found := s.values[exprID]
return val, found
}
// SetValue is an implementation of the EvalState interface method.
func (s *evalState) SetValue(exprID int64, val ref.Val) {
if val == nil {
delete(s.values, exprID)
} else {
s.values[exprID] = val
}
}
// Reset implements the EvalState interface method.
func (s *evalState) Reset() {
s.values = map[int64]ref.Val{}
}
// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package interpreter
import (
"fmt"
"sync"
"github.com/google/cel-go/common/functions"
"github.com/google/cel-go/common/operators"
"github.com/google/cel-go/common/overloads"
"github.com/google/cel-go/common/types"
"github.com/google/cel-go/common/types/ref"
"github.com/google/cel-go/common/types/traits"
)
// Interpretable can accept a given Activation and produce a value along with
// an accompanying EvalState which can be used to inspect whether additional
// data might be necessary to complete the evaluation.
type Interpretable interface {
// ID value corresponding to the expression node.
ID() int64
// Eval an Activation to produce an output.
Eval(activation Activation) ref.Val
}
// InterpretableConst interface for tracking whether the Interpretable is a constant value.
type InterpretableConst interface {
Interpretable
// Value returns the constant value of the instruction.
Value() ref.Val
}
// InterpretableAttribute interface for tracking whether the Interpretable is an attribute.
type InterpretableAttribute interface {
Interpretable
// Attr returns the Attribute value.
Attr() Attribute
// Adapter returns the type adapter to be used for adapting resolved Attribute values.
Adapter() types.Adapter
// AddQualifier proxies the Attribute.AddQualifier method.
//
// Note, this method may mutate the current attribute state. If the desire is to clone the
// Attribute, the Attribute should first be copied before adding the qualifier. Attributes
// are not copyable by default, so this is a capable that would need to be added to the
// AttributeFactory or specifically to the underlying Attribute implementation.
AddQualifier(Qualifier) (Attribute, error)
// Qualify replicates the Attribute.Qualify method to permit extension and interception
// of object qualification.
Qualify(vars Activation, obj any) (any, error)
// QualifyIfPresent qualifies the object if the qualifier is declared or defined on the object.
// The 'presenceOnly' flag indicates that the value is not necessary, just a boolean status as
// to whether the qualifier is present.
QualifyIfPresent(vars Activation, obj any, presenceOnly bool) (any, bool, error)
// IsOptional indicates whether the resulting value is an optional type.
IsOptional() bool
// Resolve returns the value of the Attribute given the current Activation.
Resolve(Activation) (any, error)
}
// InterpretableCall interface for inspecting Interpretable instructions related to function calls.
type InterpretableCall interface {
Interpretable
// Function returns the function name as it appears in text or mangled operator name as it
// appears in the operators.go file.
Function() string
// OverloadID returns the overload id associated with the function specialization.
// Overload ids are stable across language boundaries and can be treated as synonymous with a
// unique function signature.
OverloadID() string
// Args returns the normalized arguments to the function overload.
// For receiver-style functions, the receiver target is arg 0.
Args() []Interpretable
}
// InterpretableConstructor interface for inspecting Interpretable instructions that initialize a list, map
// or struct.
type InterpretableConstructor interface {
Interpretable
// InitVals returns all the list elements, map key and values or struct field values.
InitVals() []Interpretable
// Type returns the type constructed.
Type() ref.Type
}
// ObservableInterpretable is an Interpretable which supports stateful observation, such as tracing
// or cost-tracking.
type ObservableInterpretable struct {
Interpretable
observers []StatefulObserver
}
// ID implements the Interpretable method to get the expression id associated with the step.
func (oi *ObservableInterpretable) ID() int64 {
return oi.Interpretable.ID()
}
// Eval proxies to the ObserveEval method while invoking a no-op callback to report the observations.
func (oi *ObservableInterpretable) Eval(vars Activation) ref.Val {
return oi.ObserveEval(vars, func(any) {})
}
// ObserveEval evaluates an interpretable and performs per-evaluation state-tracking.
//
// This method is concurrency safe and the expectation is that the observer function will use
// a switch statement to determine the type of the state which has been reported back from the call.
func (oi *ObservableInterpretable) ObserveEval(vars Activation, observer func(any)) ref.Val {
var err error
// Initialize the state needed for the observers to function.
for _, obs := range oi.observers {
vars, err = obs.InitState(vars)
if err != nil {
return types.WrapErr(err)
}
// Provide an initial reference to the state to ensure state is available
// even in cases of interrupting errors generated during evaluation.
observer(obs.GetState(vars))
}
result := oi.Interpretable.Eval(vars)
// Get the state which needs to be reported back as having been observed.
for _, obs := range oi.observers {
observer(obs.GetState(vars))
}
return result
}
// Core Interpretable implementations used during the program planning phase.
type evalTestOnly struct {
id int64
InterpretableAttribute
}
// ID implements the Interpretable interface method.
func (test *evalTestOnly) ID() int64 {
return test.id
}
// Eval implements the Interpretable interface method.
func (test *evalTestOnly) Eval(ctx Activation) ref.Val {
val, err := test.Resolve(ctx)
// Return an error if the resolve step fails
if err != nil {
return types.LabelErrNode(test.id, types.WrapErr(err))
}
if optVal, isOpt := val.(*types.Optional); isOpt {
return types.Bool(optVal.HasValue())
}
return test.Adapter().NativeToValue(val)
}
// AddQualifier appends a qualifier that will always and only perform a presence test.
func (test *evalTestOnly) AddQualifier(q Qualifier) (Attribute, error) {
cq, ok := q.(ConstantQualifier)
if !ok {
return nil, fmt.Errorf("test only expressions must have constant qualifiers: %v", q)
}
return test.InterpretableAttribute.AddQualifier(&testOnlyQualifier{ConstantQualifier: cq})
}
type testOnlyQualifier struct {
ConstantQualifier
}
// Qualify determines whether the test-only qualifier is present on the input object.
func (q *testOnlyQualifier) Qualify(vars Activation, obj any) (any, error) {
out, present, err := q.ConstantQualifier.QualifyIfPresent(vars, obj, true)
if err != nil {
return nil, err
}
if unk, isUnk := out.(types.Unknown); isUnk {
return unk, nil
}
return present, nil
}
// QualifyIfPresent returns whether the target field in the test-only expression is present.
func (q *testOnlyQualifier) QualifyIfPresent(vars Activation, obj any, presenceOnly bool) (any, bool, error) {
// Only ever test for presence.
return q.ConstantQualifier.QualifyIfPresent(vars, obj, true)
}
// QualifierValueEquals determines whether the test-only constant qualifier equals the input value.
func (q *testOnlyQualifier) QualifierValueEquals(value any) bool {
// The input qualifier will always be of type string
return q.ConstantQualifier.Value().Value() == value
}
// NewConstValue creates a new constant valued Interpretable.
func NewConstValue(id int64, val ref.Val) InterpretableConst {
return &evalConst{
id: id,
val: val,
}
}
type evalConst struct {
id int64
val ref.Val
}
// ID implements the Interpretable interface method.
func (cons *evalConst) ID() int64 {
return cons.id
}
// Eval implements the Interpretable interface method.
func (cons *evalConst) Eval(ctx Activation) ref.Val {
return cons.val
}
// Value implements the InterpretableConst interface method.
func (cons *evalConst) Value() ref.Val {
return cons.val
}
type evalOr struct {
id int64
terms []Interpretable
}
// ID implements the Interpretable interface method.
func (or *evalOr) ID() int64 {
return or.id
}
// Eval implements the Interpretable interface method.
func (or *evalOr) Eval(ctx Activation) ref.Val {
var err ref.Val = nil
var unk *types.Unknown
for _, term := range or.terms {
val := term.Eval(ctx)
boolVal, ok := val.(types.Bool)
// short-circuit on true.
if ok && boolVal == types.True {
return types.True
}
if !ok {
isUnk := false
unk, isUnk = types.MaybeMergeUnknowns(val, unk)
if !isUnk && err == nil {
if types.IsError(val) {
err = val
} else {
err = types.MaybeNoSuchOverloadErr(val)
}
err = types.LabelErrNode(or.id, err)
}
}
}
if unk != nil {
return unk
}
if err != nil {
return err
}
return types.False
}
type evalAnd struct {
id int64
terms []Interpretable
}
// ID implements the Interpretable interface method.
func (and *evalAnd) ID() int64 {
return and.id
}
// Eval implements the Interpretable interface method.
func (and *evalAnd) Eval(ctx Activation) ref.Val {
var err ref.Val = nil
var unk *types.Unknown
for _, term := range and.terms {
val := term.Eval(ctx)
boolVal, ok := val.(types.Bool)
// short-circuit on false.
if ok && boolVal == types.False {
return types.False
}
if !ok {
isUnk := false
unk, isUnk = types.MaybeMergeUnknowns(val, unk)
if !isUnk && err == nil {
if types.IsError(val) {
err = val
} else {
err = types.MaybeNoSuchOverloadErr(val)
}
err = types.LabelErrNode(and.id, err)
}
}
}
if unk != nil {
return unk
}
if err != nil {
return err
}
return types.True
}
type evalEq struct {
id int64
lhs Interpretable
rhs Interpretable
}
// ID implements the Interpretable interface method.
func (eq *evalEq) ID() int64 {
return eq.id
}
// Eval implements the Interpretable interface method.
func (eq *evalEq) Eval(ctx Activation) ref.Val {
lVal := eq.lhs.Eval(ctx)
rVal := eq.rhs.Eval(ctx)
if types.IsUnknownOrError(lVal) {
return lVal
}
if types.IsUnknownOrError(rVal) {
return rVal
}
return types.Equal(lVal, rVal)
}
// Function implements the InterpretableCall interface method.
func (*evalEq) Function() string {
return operators.Equals
}
// OverloadID implements the InterpretableCall interface method.
func (*evalEq) OverloadID() string {
return overloads.Equals
}
// Args implements the InterpretableCall interface method.
func (eq *evalEq) Args() []Interpretable {
return []Interpretable{eq.lhs, eq.rhs}
}
type evalNe struct {
id int64
lhs Interpretable
rhs Interpretable
}
// ID implements the Interpretable interface method.
func (ne *evalNe) ID() int64 {
return ne.id
}
// Eval implements the Interpretable interface method.
func (ne *evalNe) Eval(ctx Activation) ref.Val {
lVal := ne.lhs.Eval(ctx)
rVal := ne.rhs.Eval(ctx)
if types.IsUnknownOrError(lVal) {
return lVal
}
if types.IsUnknownOrError(rVal) {
return rVal
}
return types.Bool(types.Equal(lVal, rVal) != types.True)
}
// Function implements the InterpretableCall interface method.
func (*evalNe) Function() string {
return operators.NotEquals
}
// OverloadID implements the InterpretableCall interface method.
func (*evalNe) OverloadID() string {
return overloads.NotEquals
}
// Args implements the InterpretableCall interface method.
func (ne *evalNe) Args() []Interpretable {
return []Interpretable{ne.lhs, ne.rhs}
}
type evalZeroArity struct {
id int64
function string
overload string
impl functions.FunctionOp
}
// ID implements the Interpretable interface method.
func (zero *evalZeroArity) ID() int64 {
return zero.id
}
// Eval implements the Interpretable interface method.
func (zero *evalZeroArity) Eval(ctx Activation) ref.Val {
return types.LabelErrNode(zero.id, zero.impl())
}
// Function implements the InterpretableCall interface method.
func (zero *evalZeroArity) Function() string {
return zero.function
}
// OverloadID implements the InterpretableCall interface method.
func (zero *evalZeroArity) OverloadID() string {
return zero.overload
}
// Args returns the argument to the unary function.
func (zero *evalZeroArity) Args() []Interpretable {
return []Interpretable{}
}
type evalUnary struct {
id int64
function string
overload string
arg Interpretable
trait int
impl functions.UnaryOp
nonStrict bool
}
// ID implements the Interpretable interface method.
func (un *evalUnary) ID() int64 {
return un.id
}
// Eval implements the Interpretable interface method.
func (un *evalUnary) Eval(ctx Activation) ref.Val {
argVal := un.arg.Eval(ctx)
// Early return if the argument to the function is unknown or error.
strict := !un.nonStrict
if strict && types.IsUnknownOrError(argVal) {
return argVal
}
// If the implementation is bound and the argument value has the right traits required to
// invoke it, then call the implementation.
if un.impl != nil && (un.trait == 0 || (!strict && types.IsUnknownOrError(argVal)) || argVal.Type().HasTrait(un.trait)) {
return types.LabelErrNode(un.id, un.impl(argVal))
}
// Otherwise, if the argument is a ReceiverType attempt to invoke the receiver method on the
// operand (arg0).
if argVal.Type().HasTrait(traits.ReceiverType) {
return types.LabelErrNode(un.id, argVal.(traits.Receiver).Receive(un.function, un.overload, []ref.Val{}))
}
return types.NewErrWithNodeID(un.id, "no such overload: %s", un.function)
}
// Function implements the InterpretableCall interface method.
func (un *evalUnary) Function() string {
return un.function
}
// OverloadID implements the InterpretableCall interface method.
func (un *evalUnary) OverloadID() string {
return un.overload
}
// Args returns the argument to the unary function.
func (un *evalUnary) Args() []Interpretable {
return []Interpretable{un.arg}
}
type evalBinary struct {
id int64
function string
overload string
lhs Interpretable
rhs Interpretable
trait int
impl functions.BinaryOp
nonStrict bool
}
// ID implements the Interpretable interface method.
func (bin *evalBinary) ID() int64 {
return bin.id
}
// Eval implements the Interpretable interface method.
func (bin *evalBinary) Eval(ctx Activation) ref.Val {
lVal := bin.lhs.Eval(ctx)
rVal := bin.rhs.Eval(ctx)
// Early return if any argument to the function is unknown or error.
strict := !bin.nonStrict
if strict {
if types.IsUnknownOrError(lVal) {
return lVal
}
if types.IsUnknownOrError(rVal) {
return rVal
}
}
// If the implementation is bound and the argument value has the right traits required to
// invoke it, then call the implementation.
if bin.impl != nil && (bin.trait == 0 || (!strict && types.IsUnknownOrError(lVal)) || lVal.Type().HasTrait(bin.trait)) {
return types.LabelErrNode(bin.id, bin.impl(lVal, rVal))
}
// Otherwise, if the argument is a ReceiverType attempt to invoke the receiver method on the
// operand (arg0).
if lVal.Type().HasTrait(traits.ReceiverType) {
return types.LabelErrNode(bin.id, lVal.(traits.Receiver).Receive(bin.function, bin.overload, []ref.Val{rVal}))
}
return types.NewErrWithNodeID(bin.id, "no such overload: %s", bin.function)
}
// Function implements the InterpretableCall interface method.
func (bin *evalBinary) Function() string {
return bin.function
}
// OverloadID implements the InterpretableCall interface method.
func (bin *evalBinary) OverloadID() string {
return bin.overload
}
// Args returns the argument to the unary function.
func (bin *evalBinary) Args() []Interpretable {
return []Interpretable{bin.lhs, bin.rhs}
}
type evalVarArgs struct {
id int64
function string
overload string
args []Interpretable
trait int
impl functions.FunctionOp
nonStrict bool
}
// NewCall creates a new call Interpretable.
func NewCall(id int64, function, overload string, args []Interpretable, impl functions.FunctionOp) InterpretableCall {
return &evalVarArgs{
id: id,
function: function,
overload: overload,
args: args,
impl: impl,
}
}
// ID implements the Interpretable interface method.
func (fn *evalVarArgs) ID() int64 {
return fn.id
}
// Eval implements the Interpretable interface method.
func (fn *evalVarArgs) Eval(ctx Activation) ref.Val {
argVals := make([]ref.Val, len(fn.args))
// Early return if any argument to the function is unknown or error.
strict := !fn.nonStrict
for i, arg := range fn.args {
argVals[i] = arg.Eval(ctx)
if strict && types.IsUnknownOrError(argVals[i]) {
return argVals[i]
}
}
// If the implementation is bound and the argument value has the right traits required to
// invoke it, then call the implementation.
arg0 := argVals[0]
if fn.impl != nil && (fn.trait == 0 || (!strict && types.IsUnknownOrError(arg0)) || arg0.Type().HasTrait(fn.trait)) {
return types.LabelErrNode(fn.id, fn.impl(argVals...))
}
// Otherwise, if the argument is a ReceiverType attempt to invoke the receiver method on the
// operand (arg0).
if arg0.Type().HasTrait(traits.ReceiverType) {
return types.LabelErrNode(fn.id, arg0.(traits.Receiver).Receive(fn.function, fn.overload, argVals[1:]))
}
return types.NewErrWithNodeID(fn.id, "no such overload: %s %d", fn.function, fn.id)
}
// Function implements the InterpretableCall interface method.
func (fn *evalVarArgs) Function() string {
return fn.function
}
// OverloadID implements the InterpretableCall interface method.
func (fn *evalVarArgs) OverloadID() string {
return fn.overload
}
// Args returns the argument to the unary function.
func (fn *evalVarArgs) Args() []Interpretable {
return fn.args
}
type evalList struct {
id int64
elems []Interpretable
optionals []bool
hasOptionals bool
adapter types.Adapter
}
// ID implements the Interpretable interface method.
func (l *evalList) ID() int64 {
return l.id
}
// Eval implements the Interpretable interface method.
func (l *evalList) Eval(ctx Activation) ref.Val {
elemVals := make([]ref.Val, 0, len(l.elems))
// If any argument is unknown or error early terminate.
for i, elem := range l.elems {
elemVal := elem.Eval(ctx)
if types.IsUnknownOrError(elemVal) {
return elemVal
}
if l.hasOptionals && l.optionals[i] {
optVal, ok := elemVal.(*types.Optional)
if !ok {
return types.LabelErrNode(l.id, invalidOptionalElementInit(elemVal))
}
if !optVal.HasValue() {
continue
}
elemVal = optVal.GetValue()
}
elemVals = append(elemVals, elemVal)
}
return l.adapter.NativeToValue(elemVals)
}
func (l *evalList) InitVals() []Interpretable {
return l.elems
}
func (l *evalList) Type() ref.Type {
return types.ListType
}
type evalMap struct {
id int64
keys []Interpretable
vals []Interpretable
optionals []bool
hasOptionals bool
adapter types.Adapter
}
// ID implements the Interpretable interface method.
func (m *evalMap) ID() int64 {
return m.id
}
// Eval implements the Interpretable interface method.
func (m *evalMap) Eval(ctx Activation) ref.Val {
entries := make(map[ref.Val]ref.Val)
// If any argument is unknown or error early terminate.
for i, key := range m.keys {
keyVal := key.Eval(ctx)
if types.IsUnknownOrError(keyVal) {
return keyVal
}
valVal := m.vals[i].Eval(ctx)
if types.IsUnknownOrError(valVal) {
return valVal
}
if m.hasOptionals && m.optionals[i] {
optVal, ok := valVal.(*types.Optional)
if !ok {
return types.LabelErrNode(m.id, invalidOptionalEntryInit(keyVal, valVal))
}
if !optVal.HasValue() {
delete(entries, keyVal)
continue
}
valVal = optVal.GetValue()
}
entries[keyVal] = valVal
}
return m.adapter.NativeToValue(entries)
}
func (m *evalMap) InitVals() []Interpretable {
if len(m.keys) != len(m.vals) {
return nil
}
result := make([]Interpretable, len(m.keys)+len(m.vals))
idx := 0
for i, k := range m.keys {
v := m.vals[i]
result[idx] = k
idx++
result[idx] = v
idx++
}
return result
}
func (m *evalMap) Type() ref.Type {
return types.MapType
}
type evalObj struct {
id int64
typeName string
fields []string
vals []Interpretable
optionals []bool
hasOptionals bool
provider types.Provider
}
// ID implements the Interpretable interface method.
func (o *evalObj) ID() int64 {
return o.id
}
// Eval implements the Interpretable interface method.
func (o *evalObj) Eval(ctx Activation) ref.Val {
fieldVals := make(map[string]ref.Val)
// If any argument is unknown or error early terminate.
for i, field := range o.fields {
val := o.vals[i].Eval(ctx)
if types.IsUnknownOrError(val) {
return val
}
if o.hasOptionals && o.optionals[i] {
optVal, ok := val.(*types.Optional)
if !ok {
return types.LabelErrNode(o.id, invalidOptionalEntryInit(field, val))
}
if !optVal.HasValue() {
delete(fieldVals, field)
continue
}
val = optVal.GetValue()
}
fieldVals[field] = val
}
return types.LabelErrNode(o.id, o.provider.NewValue(o.typeName, fieldVals))
}
// InitVals implements the InterpretableConstructor interface method.
func (o *evalObj) InitVals() []Interpretable {
return o.vals
}
// Type implements the InterpretableConstructor interface method.
func (o *evalObj) Type() ref.Type {
return types.NewObjectType(o.typeName)
}
type evalFold struct {
id int64
accuVar string
iterVar string
iterVar2 string
iterRange Interpretable
accu Interpretable
cond Interpretable
step Interpretable
result Interpretable
adapter types.Adapter
// note an exhaustive fold will ensure that all branches are evaluated
// when using mutable values, these branches will mutate the final result
// rather than make a throw-away computation.
exhaustive bool
interruptable bool
}
// ID implements the Interpretable interface method.
func (fold *evalFold) ID() int64 {
return fold.id
}
// Eval implements the Interpretable interface method.
func (fold *evalFold) Eval(ctx Activation) ref.Val {
// Initialize the folder interface
f := newFolder(fold, ctx)
defer releaseFolder(f)
foldRange := fold.iterRange.Eval(ctx)
if types.IsUnknownOrError(foldRange) {
return foldRange
}
if fold.iterVar2 != "" {
var foldable traits.Foldable
switch r := foldRange.(type) {
case traits.Mapper:
foldable = types.ToFoldableMap(r)
case traits.Lister:
foldable = types.ToFoldableList(r)
default:
return types.NewErrWithNodeID(fold.ID(), "unsupported comprehension range type: %T", foldRange)
}
foldable.Fold(f)
return f.evalResult()
}
if !foldRange.Type().HasTrait(traits.IterableType) {
return types.ValOrErr(foldRange, "got '%T', expected iterable type", foldRange)
}
iterable := foldRange.(traits.Iterable)
return f.foldIterable(iterable)
}
// Optional Interpretable implementations that specialize, subsume, or extend the core evaluation
// plan via decorators.
// evalSetMembership is an Interpretable implementation which tests whether an input value
// exists within the set of map keys used to model a set.
type evalSetMembership struct {
inst Interpretable
arg Interpretable
valueSet map[ref.Val]ref.Val
}
// ID implements the Interpretable interface method.
func (e *evalSetMembership) ID() int64 {
return e.inst.ID()
}
// Eval implements the Interpretable interface method.
func (e *evalSetMembership) Eval(ctx Activation) ref.Val {
val := e.arg.Eval(ctx)
if types.IsUnknownOrError(val) {
return val
}
if ret, found := e.valueSet[val]; found {
return ret
}
return types.False
}
// evalWatch is an Interpretable implementation that wraps the execution of a given
// expression so that it may observe the computed value and send it to an observer.
type evalWatch struct {
Interpretable
observer EvalObserver
}
// Eval implements the Interpretable interface method.
func (e *evalWatch) Eval(vars Activation) ref.Val {
val := e.Interpretable.Eval(vars)
e.observer(vars, e.ID(), e.Interpretable, val)
return val
}
// evalWatchAttr describes a watcher of an InterpretableAttribute Interpretable.
//
// Since the watcher may be selected against at a later stage in program planning, the watcher
// must implement the InterpretableAttribute interface by proxy.
type evalWatchAttr struct {
InterpretableAttribute
observer EvalObserver
}
// AddQualifier creates a wrapper over the incoming qualifier which observes the qualification
// result.
func (e *evalWatchAttr) AddQualifier(q Qualifier) (Attribute, error) {
switch qual := q.(type) {
// By default, the qualifier is either a constant or an attribute
// There may be some custom cases where the attribute is neither.
case ConstantQualifier:
// Expose a method to test whether the qualifier matches the input pattern.
q = &evalWatchConstQual{
ConstantQualifier: qual,
observer: e.observer,
adapter: e.Adapter(),
}
case *evalWatchAttr:
// Unwrap the evalWatchAttr since the observation will be applied during Qualify or
// QualifyIfPresent rather than Eval.
q = &evalWatchAttrQual{
Attribute: qual.InterpretableAttribute,
observer: e.observer,
adapter: e.Adapter(),
}
case Attribute:
// Expose methods which intercept the qualification prior to being applied as a qualifier.
// Using this interface ensures that the qualifier is converted to a constant value one
// time during attribute pattern matching as the method embeds the Attribute interface
// needed to trip the conversion to a constant.
q = &evalWatchAttrQual{
Attribute: qual,
observer: e.observer,
adapter: e.Adapter(),
}
default:
// This is likely a custom qualifier type.
q = &evalWatchQual{
Qualifier: qual,
observer: e.observer,
adapter: e.Adapter(),
}
}
_, err := e.InterpretableAttribute.AddQualifier(q)
return e, err
}
// Eval implements the Interpretable interface method.
func (e *evalWatchAttr) Eval(vars Activation) ref.Val {
val := e.InterpretableAttribute.Eval(vars)
e.observer(vars, e.ID(), e.InterpretableAttribute, val)
return val
}
// evalWatchConstQual observes the qualification of an object using a constant boolean, int,
// string, or uint.
type evalWatchConstQual struct {
ConstantQualifier
observer EvalObserver
adapter types.Adapter
}
// Qualify observes the qualification of a object via a constant boolean, int, string, or uint.
func (e *evalWatchConstQual) Qualify(vars Activation, obj any) (any, error) {
out, err := e.ConstantQualifier.Qualify(vars, obj)
var val ref.Val
if err != nil {
val = types.LabelErrNode(e.ID(), types.WrapErr(err))
} else {
val = e.adapter.NativeToValue(out)
}
e.observer(vars, e.ID(), e.ConstantQualifier, val)
return out, err
}
// QualifyIfPresent conditionally qualifies the variable and only records a value if one is present.
func (e *evalWatchConstQual) QualifyIfPresent(vars Activation, obj any, presenceOnly bool) (any, bool, error) {
out, present, err := e.ConstantQualifier.QualifyIfPresent(vars, obj, presenceOnly)
var val ref.Val
if err != nil {
val = types.LabelErrNode(e.ID(), types.WrapErr(err))
} else if out != nil {
val = e.adapter.NativeToValue(out)
} else if presenceOnly {
val = types.Bool(present)
}
if present || presenceOnly {
e.observer(vars, e.ID(), e.ConstantQualifier, val)
}
return out, present, err
}
// QualifierValueEquals tests whether the incoming value is equal to the qualifying constant.
func (e *evalWatchConstQual) QualifierValueEquals(value any) bool {
qve, ok := e.ConstantQualifier.(qualifierValueEquator)
return ok && qve.QualifierValueEquals(value)
}
// evalWatchAttrQual observes the qualification of an object by a value computed at runtime.
type evalWatchAttrQual struct {
Attribute
observer EvalObserver
adapter ref.TypeAdapter
}
// Qualify observes the qualification of a object via a value computed at runtime.
func (e *evalWatchAttrQual) Qualify(vars Activation, obj any) (any, error) {
out, err := e.Attribute.Qualify(vars, obj)
var val ref.Val
if err != nil {
val = types.LabelErrNode(e.ID(), types.WrapErr(err))
} else {
val = e.adapter.NativeToValue(out)
}
e.observer(vars, e.ID(), e.Attribute, val)
return out, err
}
// QualifyIfPresent conditionally qualifies the variable and only records a value if one is present.
func (e *evalWatchAttrQual) QualifyIfPresent(vars Activation, obj any, presenceOnly bool) (any, bool, error) {
out, present, err := e.Attribute.QualifyIfPresent(vars, obj, presenceOnly)
var val ref.Val
if err != nil {
val = types.LabelErrNode(e.ID(), types.WrapErr(err))
} else if out != nil {
val = e.adapter.NativeToValue(out)
} else if presenceOnly {
val = types.Bool(present)
}
if present || presenceOnly {
e.observer(vars, e.ID(), e.Attribute, val)
}
return out, present, err
}
// evalWatchQual observes the qualification of an object by a value computed at runtime.
type evalWatchQual struct {
Qualifier
observer EvalObserver
adapter types.Adapter
}
// Qualify observes the qualification of a object via a value computed at runtime.
func (e *evalWatchQual) Qualify(vars Activation, obj any) (any, error) {
out, err := e.Qualifier.Qualify(vars, obj)
var val ref.Val
if err != nil {
val = types.LabelErrNode(e.ID(), types.WrapErr(err))
} else {
val = e.adapter.NativeToValue(out)
}
e.observer(vars, e.ID(), e.Qualifier, val)
return out, err
}
// QualifyIfPresent conditionally qualifies the variable and only records a value if one is present.
func (e *evalWatchQual) QualifyIfPresent(vars Activation, obj any, presenceOnly bool) (any, bool, error) {
out, present, err := e.Qualifier.QualifyIfPresent(vars, obj, presenceOnly)
var val ref.Val
if err != nil {
val = types.LabelErrNode(e.ID(), types.WrapErr(err))
} else if out != nil {
val = e.adapter.NativeToValue(out)
} else if presenceOnly {
val = types.Bool(present)
}
if present || presenceOnly {
e.observer(vars, e.ID(), e.Qualifier, val)
}
return out, present, err
}
// evalWatchConst describes a watcher of an instConst Interpretable.
type evalWatchConst struct {
InterpretableConst
observer EvalObserver
}
// Eval implements the Interpretable interface method.
func (e *evalWatchConst) Eval(vars Activation) ref.Val {
val := e.Value()
e.observer(vars, e.ID(), e.InterpretableConst, val)
return val
}
// evalExhaustiveOr is just like evalOr, but does not short-circuit argument evaluation.
type evalExhaustiveOr struct {
id int64
terms []Interpretable
}
// ID implements the Interpretable interface method.
func (or *evalExhaustiveOr) ID() int64 {
return or.id
}
// Eval implements the Interpretable interface method.
func (or *evalExhaustiveOr) Eval(ctx Activation) ref.Val {
var err ref.Val = nil
var unk *types.Unknown
isTrue := false
for _, term := range or.terms {
val := term.Eval(ctx)
boolVal, ok := val.(types.Bool)
// flag the result as true
if ok && boolVal == types.True {
isTrue = true
}
if !ok && !isTrue {
isUnk := false
unk, isUnk = types.MaybeMergeUnknowns(val, unk)
if !isUnk && err == nil {
if types.IsError(val) {
err = val
} else {
err = types.MaybeNoSuchOverloadErr(val)
}
}
}
}
if isTrue {
return types.True
}
if unk != nil {
return unk
}
if err != nil {
return err
}
return types.False
}
// evalExhaustiveAnd is just like evalAnd, but does not short-circuit argument evaluation.
type evalExhaustiveAnd struct {
id int64
terms []Interpretable
}
// ID implements the Interpretable interface method.
func (and *evalExhaustiveAnd) ID() int64 {
return and.id
}
// Eval implements the Interpretable interface method.
func (and *evalExhaustiveAnd) Eval(ctx Activation) ref.Val {
var err ref.Val = nil
var unk *types.Unknown
isFalse := false
for _, term := range and.terms {
val := term.Eval(ctx)
boolVal, ok := val.(types.Bool)
// short-circuit on false.
if ok && boolVal == types.False {
isFalse = true
}
if !ok && !isFalse {
isUnk := false
unk, isUnk = types.MaybeMergeUnknowns(val, unk)
if !isUnk && err == nil {
if types.IsError(val) {
err = val
} else {
err = types.MaybeNoSuchOverloadErr(val)
}
}
}
}
if isFalse {
return types.False
}
if unk != nil {
return unk
}
if err != nil {
return err
}
return types.True
}
// evalExhaustiveConditional is like evalConditional, but does not short-circuit argument
// evaluation.
type evalExhaustiveConditional struct {
id int64
adapter types.Adapter
attr *conditionalAttribute
}
// ID implements the Interpretable interface method.
func (cond *evalExhaustiveConditional) ID() int64 {
return cond.id
}
// Eval implements the Interpretable interface method.
func (cond *evalExhaustiveConditional) Eval(ctx Activation) ref.Val {
cVal := cond.attr.expr.Eval(ctx)
tVal, tErr := cond.attr.truthy.Resolve(ctx)
fVal, fErr := cond.attr.falsy.Resolve(ctx)
cBool, ok := cVal.(types.Bool)
if !ok {
return types.ValOrErr(cVal, "no such overload")
}
if cBool {
if tErr != nil {
return types.LabelErrNode(cond.id, types.WrapErr(tErr))
}
return cond.adapter.NativeToValue(tVal)
}
if fErr != nil {
return types.LabelErrNode(cond.id, types.WrapErr(fErr))
}
return cond.adapter.NativeToValue(fVal)
}
// evalAttr evaluates an Attribute value.
type evalAttr struct {
adapter types.Adapter
attr Attribute
optional bool
}
var _ InterpretableAttribute = &evalAttr{}
// ID of the attribute instruction.
func (a *evalAttr) ID() int64 {
return a.attr.ID()
}
// AddQualifier implements the InterpretableAttribute interface method.
func (a *evalAttr) AddQualifier(qual Qualifier) (Attribute, error) {
attr, err := a.attr.AddQualifier(qual)
a.attr = attr
return attr, err
}
// Attr implements the InterpretableAttribute interface method.
func (a *evalAttr) Attr() Attribute {
return a.attr
}
// Adapter implements the InterpretableAttribute interface method.
func (a *evalAttr) Adapter() types.Adapter {
return a.adapter
}
// Eval implements the Interpretable interface method.
func (a *evalAttr) Eval(ctx Activation) ref.Val {
v, err := a.attr.Resolve(ctx)
if err != nil {
return types.LabelErrNode(a.ID(), types.WrapErr(err))
}
return a.adapter.NativeToValue(v)
}
// Qualify proxies to the Attribute's Qualify method.
func (a *evalAttr) Qualify(vars Activation, obj any) (any, error) {
return a.attr.Qualify(vars, obj)
}
// QualifyIfPresent proxies to the Attribute's QualifyIfPresent method.
func (a *evalAttr) QualifyIfPresent(vars Activation, obj any, presenceOnly bool) (any, bool, error) {
return a.attr.QualifyIfPresent(vars, obj, presenceOnly)
}
func (a *evalAttr) IsOptional() bool {
return a.optional
}
// Resolve proxies to the Attribute's Resolve method.
func (a *evalAttr) Resolve(ctx Activation) (any, error) {
return a.attr.Resolve(ctx)
}
type evalWatchConstructor struct {
constructor InterpretableConstructor
observer EvalObserver
}
// InitVals implements the InterpretableConstructor InitVals function.
func (c *evalWatchConstructor) InitVals() []Interpretable {
return c.constructor.InitVals()
}
// Type implements the InterpretableConstructor Type function.
func (c *evalWatchConstructor) Type() ref.Type {
return c.constructor.Type()
}
// ID implements the Interpretable ID function.
func (c *evalWatchConstructor) ID() int64 {
return c.constructor.ID()
}
// Eval implements the Interpretable Eval function.
func (c *evalWatchConstructor) Eval(vars Activation) ref.Val {
val := c.constructor.Eval(vars)
c.observer(vars, c.ID(), c.constructor, val)
return val
}
func invalidOptionalEntryInit(field any, value ref.Val) ref.Val {
return types.NewErr("cannot initialize optional entry '%v' from non-optional value %v", field, value)
}
func invalidOptionalElementInit(value ref.Val) ref.Val {
return types.NewErr("cannot initialize optional list element from non-optional value %v", value)
}
// newFolder creates or initializes a pooled folder instance.
func newFolder(eval *evalFold, ctx Activation) *folder {
f := folderPool.Get().(*folder)
f.evalFold = eval
f.activation = ctx
return f
}
// releaseFolder resets and releases a pooled folder instance.
func releaseFolder(f *folder) {
f.reset()
folderPool.Put(f)
}
// folder tracks the state associated with folding a list or map with a comprehension v2 style macro.
//
// The folder embeds an interpreter.Activation and Interpretable evalFold value as well as implements
// the traits.Folder interface methods.
//
// Instances of a folder are intended to be pooled to minimize allocation overhead with this temporary
// bookkeeping object which supports lazy evaluation of the accumulator init expression which is useful
// in preserving evaluation order semantics which might otherwise be disrupted through the use of
// cel.bind or cel.@block.
type folder struct {
*evalFold
activation Activation
// fold state objects.
accuVal ref.Val
iterVar1Val any
iterVar2Val any
// bookkeeping flags to modify Activation and fold behaviors.
initialized bool
mutableValue bool
interrupted bool
computeResult bool
}
func (f *folder) foldIterable(iterable traits.Iterable) ref.Val {
it := iterable.Iterator()
for it.HasNext() == types.True {
f.iterVar1Val = it.Next()
cond := f.cond.Eval(f)
condBool, ok := cond.(types.Bool)
if f.interrupted || (!f.exhaustive && ok && condBool != types.True) {
return f.evalResult()
}
// Update the accumulation value and check for eval interuption.
f.accuVal = f.step.Eval(f)
f.initialized = true
if f.interruptable && checkInterrupt(f.activation) {
f.interrupted = true
return f.evalResult()
}
}
return f.evalResult()
}
// FoldEntry will either fold comprehension v1 style macros if iterVar2 is unset, or comprehension v2 style
// macros if both the iterVar and iterVar2 are set to non-empty strings.
func (f *folder) FoldEntry(key, val any) bool {
// Default to referencing both values.
f.iterVar1Val = key
f.iterVar2Val = val
// Terminate evaluation if evaluation is interrupted or the condition is not true and exhaustive
// eval is not enabled.
cond := f.cond.Eval(f)
condBool, ok := cond.(types.Bool)
if f.interrupted || (!f.exhaustive && ok && condBool != types.True) {
return false
}
// Update the accumulation value and check for eval interuption.
f.accuVal = f.step.Eval(f)
f.initialized = true
if f.interruptable && checkInterrupt(f.activation) {
f.interrupted = true
return false
}
return true
}
// ResolveName overrides the default Activation lookup to perform lazy initialization of the accumulator
// and specialized lookups of iteration values with consideration for whether the final result is being
// computed and the iteration variables should be ignored.
func (f *folder) ResolveName(name string) (any, bool) {
if name == f.accuVar {
if !f.initialized {
f.initialized = true
initVal := f.accu.Eval(f.activation)
if !f.exhaustive {
if l, isList := initVal.(traits.Lister); isList && l.Size() == types.IntZero {
initVal = types.NewMutableList(f.adapter)
f.mutableValue = true
}
if m, isMap := initVal.(traits.Mapper); isMap && m.Size() == types.IntZero {
initVal = types.NewMutableMap(f.adapter, map[ref.Val]ref.Val{})
f.mutableValue = true
}
}
f.accuVal = initVal
}
return f.accuVal, true
}
if !f.computeResult {
if name == f.iterVar {
f.iterVar1Val = f.adapter.NativeToValue(f.iterVar1Val)
return f.iterVar1Val, true
}
if name == f.iterVar2 {
f.iterVar2Val = f.adapter.NativeToValue(f.iterVar2Val)
return f.iterVar2Val, true
}
}
return f.activation.ResolveName(name)
}
// Parent returns the activation embedded into the folder.
func (f *folder) Parent() Activation {
return f.activation
}
// UnknownAttributePatterns implements the PartialActivation interface returning the unknown patterns
// if they were provided to the input activation, or an empty set if the proxied activation is not partial.
func (f *folder) UnknownAttributePatterns() []*AttributePattern {
if pv, ok := f.activation.(partialActivationConverter); ok {
if partial, isPartial := pv.AsPartialActivation(); isPartial {
return partial.UnknownAttributePatterns()
}
}
return []*AttributePattern{}
}
func (f *folder) AsPartialActivation() (PartialActivation, bool) {
if pv, ok := f.activation.(partialActivationConverter); ok {
if _, isPartial := pv.AsPartialActivation(); isPartial {
return f, true
}
}
return nil, false
}
// evalResult computes the final result of the fold after all entries have been folded and accumulated.
func (f *folder) evalResult() ref.Val {
f.computeResult = true
if f.interrupted {
return types.NewErr("operation interrupted")
}
res := f.result.Eval(f)
// Convert a mutable list or map to an immutable one if the comprehension has generated a list or
// map as a result.
if !types.IsUnknownOrError(res) && f.mutableValue {
if _, ok := res.(traits.MutableLister); ok {
res = res.(traits.MutableLister).ToImmutableList()
}
if _, ok := res.(traits.MutableMapper); ok {
res = res.(traits.MutableMapper).ToImmutableMap()
}
}
return res
}
// reset clears any state associated with folder evaluation.
func (f *folder) reset() {
f.evalFold = nil
f.activation = nil
f.accuVal = nil
f.iterVar1Val = nil
f.iterVar2Val = nil
f.initialized = false
f.mutableValue = false
f.interrupted = false
f.computeResult = false
}
func checkInterrupt(a Activation) bool {
stop, found := a.ResolveName("#interrupted")
return found && stop == true
}
var (
// pool of var folders to reduce allocations during folds.
folderPool = &sync.Pool{
New: func() any {
return &folder{}
},
}
)
// Copyright 2018 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Package interpreter provides functions to evaluate parsed expressions with
// the option to augment the evaluation with inputs and functions supplied at
// evaluation time.
package interpreter
import (
"errors"
"github.com/google/cel-go/common/ast"
"github.com/google/cel-go/common/containers"
"github.com/google/cel-go/common/types"
"github.com/google/cel-go/common/types/ref"
)
// PlannerOption configures the program plan options during interpretable setup.
type PlannerOption func(*planner) (*planner, error)
// Interpreter generates a new Interpretable from a checked or unchecked expression.
type Interpreter interface {
// NewInterpretable creates an Interpretable from a checked expression and an
// optional list of PlannerOption values.
NewInterpretable(exprAST *ast.AST, opts ...PlannerOption) (Interpretable, error)
}
// EvalObserver is a functional interface that accepts an expression id and an observed value.
// The id identifies the expression that was evaluated, the programStep is the Interpretable or Qualifier that
// was evaluated and value is the result of the evaluation.
type EvalObserver func(vars Activation, id int64, programStep any, value ref.Val)
// StatefulObserver observes evaluation while tracking or utilizing stateful behavior.
type StatefulObserver interface {
// InitState configures stateful metadata on the activation.
InitState(Activation) (Activation, error)
// GetState retrieves the stateful metadata from the activation.
GetState(Activation) any
// Observe passes the activation and relevant evaluation metadata to the observer.
// The observe method is expected to do the equivalent of GetState(vars) in order
// to find the metadata that needs to be updated upon invocation.
Observe(vars Activation, id int64, programStep any, value ref.Val)
}
// EvalCancelledError represents a cancelled program evaluation operation.
type EvalCancelledError struct {
Message string
// Type identifies the cause of the cancellation.
Cause CancellationCause
}
func (e EvalCancelledError) Error() string {
return e.Message
}
// CancellationCause enumerates the ways a program evaluation operation can be cancelled.
type CancellationCause int
const (
// ContextCancelled indicates that the operation was cancelled in response to a Golang context cancellation.
ContextCancelled CancellationCause = iota
// CostLimitExceeded indicates that the operation was cancelled in response to the actual cost limit being
// exceeded.
CostLimitExceeded
)
// evalStateOption configures the evalStateFactory behavior.
type evalStateOption func(*evalStateFactory) *evalStateFactory
// EvalStateFactory configures the EvalState generator to be used by the EvalStateObserver.
func EvalStateFactory(factory func() EvalState) evalStateOption {
return func(fac *evalStateFactory) *evalStateFactory {
fac.factory = factory
return fac
}
}
// EvalStateObserver provides an observer which records the value associated with the given expression id.
// EvalState must be provided to the observer.
func EvalStateObserver(opts ...evalStateOption) PlannerOption {
et := &evalStateFactory{factory: NewEvalState}
for _, o := range opts {
et = o(et)
}
return func(p *planner) (*planner, error) {
if et.factory == nil {
return nil, errors.New("eval state factory not configured")
}
p.observers = append(p.observers, et)
p.decorators = append(p.decorators, decObserveEval(et.Observe))
return p, nil
}
}
// evalStateConverter identifies an object which is convertible to an EvalState instance.
type evalStateConverter interface {
asEvalState() EvalState
}
// evalStateActivation hides state in the Activation in a manner not accessible to expressions.
type evalStateActivation struct {
vars Activation
state EvalState
}
// ResolveName proxies variable lookups to the backing activation.
func (esa evalStateActivation) ResolveName(name string) (any, bool) {
return esa.vars.ResolveName(name)
}
// Parent proxies parent lookups to the backing activation.
func (esa evalStateActivation) Parent() Activation {
return esa.vars
}
// AsPartialActivation supports conversion to a partial activation in order to detect unknown attributes.
func (esa evalStateActivation) AsPartialActivation() (PartialActivation, bool) {
return AsPartialActivation(esa.vars)
}
// asEvalState implements the evalStateConverter method.
func (esa evalStateActivation) asEvalState() EvalState {
return esa.state
}
// asEvalState walks the Activation hierarchy and returns the first EvalState found, if present.
func asEvalState(vars Activation) (EvalState, bool) {
if conv, ok := vars.(evalStateConverter); ok {
return conv.asEvalState(), true
}
if vars.Parent() != nil {
return asEvalState(vars.Parent())
}
return nil, false
}
// evalStateFactory holds a reference to a factory function that produces an EvalState instance.
type evalStateFactory struct {
factory func() EvalState
}
// InitState produces an EvalState instance and bundles it into the Activation in a way which is
// not visible to expression evaluation.
func (et *evalStateFactory) InitState(vars Activation) (Activation, error) {
state := et.factory()
return evalStateActivation{vars: vars, state: state}, nil
}
// GetState extracts the EvalState from the Activation.
func (et *evalStateFactory) GetState(vars Activation) any {
if state, found := asEvalState(vars); found {
return state
}
return nil
}
// Observe records the evaluation state for a given expression node and program step.
func (et *evalStateFactory) Observe(vars Activation, id int64, programStep any, val ref.Val) {
state, found := asEvalState(vars)
if !found {
return
}
state.SetValue(id, val)
}
// CustomDecorator configures a custom interpretable decorator for the program.
func CustomDecorator(dec InterpretableDecorator) PlannerOption {
return func(p *planner) (*planner, error) {
p.decorators = append(p.decorators, dec)
return p, nil
}
}
// ExhaustiveEval replaces operations that short-circuit with versions that evaluate
// expressions and couples this behavior with the TrackState() decorator to provide
// insight into the evaluation state of the entire expression. EvalState must be
// provided to the decorator. This decorator is not thread-safe, and the EvalState
// must be reset between Eval() calls.
func ExhaustiveEval() PlannerOption {
return CustomDecorator(decDisableShortcircuits())
}
// InterruptableEval annotates comprehension loops with information that indicates they
// should check the `#interrupted` state within a custom Activation.
//
// The custom activation is currently managed higher up in the stack within the 'cel' package
// and should not require any custom support on behalf of callers.
func InterruptableEval() PlannerOption {
return CustomDecorator(decInterruptFolds())
}
// Optimize will pre-compute operations such as list and map construction and optimize
// call arguments to set membership tests. The set of optimizations will increase over time.
func Optimize() PlannerOption {
return CustomDecorator(decOptimize())
}
// RegexOptimization provides a way to replace an InterpretableCall for a regex function when the
// RegexIndex argument is a string constant. Typically, the Factory would compile the regex pattern at
// RegexIndex and report any errors (at program creation time) and then use the compiled regex for
// all regex function invocations.
type RegexOptimization struct {
// Function is the name of the function to optimize.
Function string
// OverloadID is the ID of the overload to optimize.
OverloadID string
// RegexIndex is the index position of the regex pattern argument. Only calls to the function where this argument is
// a string constant will be delegated to this optimizer.
RegexIndex int
// Factory constructs a replacement InterpretableCall node that optimizes the regex function call. Factory is
// provided with the unoptimized regex call and the string constant at the RegexIndex argument.
// The Factory may compile the regex for use across all invocations of the call, return any errors and
// return an interpreter.NewCall with the desired regex optimized function impl.
Factory func(call InterpretableCall, regexPattern string) (InterpretableCall, error)
}
// CompileRegexConstants compiles regex pattern string constants at program creation time and reports any regex pattern
// compile errors.
func CompileRegexConstants(regexOptimizations ...*RegexOptimization) PlannerOption {
return CustomDecorator(decRegexOptimizer(regexOptimizations...))
}
type exprInterpreter struct {
dispatcher Dispatcher
container *containers.Container
provider types.Provider
adapter types.Adapter
attrFactory AttributeFactory
}
// NewInterpreter builds an Interpreter from a Dispatcher and TypeProvider which will be used
// throughout the Eval of all Interpretable instances generated from it.
func NewInterpreter(dispatcher Dispatcher,
container *containers.Container,
provider types.Provider,
adapter types.Adapter,
attrFactory AttributeFactory) Interpreter {
return &exprInterpreter{
dispatcher: dispatcher,
container: container,
provider: provider,
adapter: adapter,
attrFactory: attrFactory}
}
// NewIntepretable implements the Interpreter interface method.
func (i *exprInterpreter) NewInterpretable(
checked *ast.AST,
opts ...PlannerOption) (Interpretable, error) {
p := newPlanner(i.dispatcher, i.provider, i.adapter, i.attrFactory, i.container, checked)
var err error
for _, o := range opts {
p, err = o(p)
if err != nil {
return nil, err
}
}
return p.Plan(checked.Expr())
}
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package interpreter
import (
"regexp"
"github.com/google/cel-go/common/types"
"github.com/google/cel-go/common/types/ref"
)
// MatchesRegexOptimization optimizes the 'matches' standard library function by compiling the regex pattern and
// reporting any compilation errors at program creation time, and using the compiled regex pattern for all function
// call invocations.
var MatchesRegexOptimization = &RegexOptimization{
Function: "matches",
RegexIndex: 1,
Factory: func(call InterpretableCall, regexPattern string) (InterpretableCall, error) {
compiledRegex, err := regexp.Compile(regexPattern)
if err != nil {
return nil, err
}
return NewCall(call.ID(), call.Function(), call.OverloadID(), call.Args(), func(values ...ref.Val) ref.Val {
if len(values) != 2 {
return types.NoSuchOverloadErr()
}
in, ok := values[0].Value().(string)
if !ok {
return types.NoSuchOverloadErr()
}
return types.Bool(compiledRegex.MatchString(in))
}), nil
},
}
// Copyright 2018 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package interpreter
import (
"fmt"
"strings"
"github.com/google/cel-go/common/ast"
"github.com/google/cel-go/common/containers"
"github.com/google/cel-go/common/functions"
"github.com/google/cel-go/common/operators"
"github.com/google/cel-go/common/types"
)
// newPlanner creates an interpretablePlanner which references a Dispatcher, TypeProvider,
// TypeAdapter, Container, and CheckedExpr value. These pieces of data are used to resolve
// functions, types, and namespaced identifiers at plan time rather than at runtime since
// it only needs to be done once and may be semi-expensive to compute.
func newPlanner(disp Dispatcher,
provider types.Provider,
adapter types.Adapter,
attrFactory AttributeFactory,
cont *containers.Container,
exprAST *ast.AST) *planner {
return &planner{
disp: disp,
provider: provider,
adapter: adapter,
attrFactory: attrFactory,
container: cont,
refMap: exprAST.ReferenceMap(),
typeMap: exprAST.TypeMap(),
decorators: make([]InterpretableDecorator, 0),
observers: make([]StatefulObserver, 0),
}
}
// planner is an implementation of the interpretablePlanner interface.
type planner struct {
disp Dispatcher
provider types.Provider
adapter types.Adapter
attrFactory AttributeFactory
container *containers.Container
refMap map[int64]*ast.ReferenceInfo
typeMap map[int64]*types.Type
decorators []InterpretableDecorator
observers []StatefulObserver
}
// Plan implements the interpretablePlanner interface. This implementation of the Plan method also
// applies decorators to each Interpretable generated as part of the overall plan. Decorators are
// useful for layering functionality into the evaluation that is not natively understood by CEL,
// such as state-tracking, expression re-write, and possibly efficient thread-safe memoization of
// repeated expressions.
func (p *planner) Plan(expr ast.Expr) (Interpretable, error) {
i, err := p.plan(expr)
if err != nil {
return nil, err
}
if len(p.observers) == 0 {
return i, nil
}
return &ObservableInterpretable{Interpretable: i, observers: p.observers}, nil
}
func (p *planner) plan(expr ast.Expr) (Interpretable, error) {
switch expr.Kind() {
case ast.CallKind:
return p.decorate(p.planCall(expr))
case ast.IdentKind:
return p.decorate(p.planIdent(expr))
case ast.LiteralKind:
return p.decorate(p.planConst(expr))
case ast.SelectKind:
return p.decorate(p.planSelect(expr))
case ast.ListKind:
return p.decorate(p.planCreateList(expr))
case ast.MapKind:
return p.decorate(p.planCreateMap(expr))
case ast.StructKind:
return p.decorate(p.planCreateStruct(expr))
case ast.ComprehensionKind:
return p.decorate(p.planComprehension(expr))
}
return nil, fmt.Errorf("unsupported expr: %v", expr)
}
// decorate applies the InterpretableDecorator functions to the given Interpretable.
// Both the Interpretable and error generated by a Plan step are accepted as arguments
// for convenience.
func (p *planner) decorate(i Interpretable, err error) (Interpretable, error) {
if err != nil {
return nil, err
}
for _, dec := range p.decorators {
i, err = dec(i)
if err != nil {
return nil, err
}
}
return i, nil
}
// planIdent creates an Interpretable that resolves an identifier from an Activation.
func (p *planner) planIdent(expr ast.Expr) (Interpretable, error) {
// Establish whether the identifier is in the reference map.
if identRef, found := p.refMap[expr.ID()]; found {
return p.planCheckedIdent(expr.ID(), identRef)
}
// Create the possible attribute list for the unresolved reference.
ident := expr.AsIdent()
return &evalAttr{
adapter: p.adapter,
attr: p.attrFactory.MaybeAttribute(expr.ID(), ident),
}, nil
}
func (p *planner) planCheckedIdent(id int64, identRef *ast.ReferenceInfo) (Interpretable, error) {
// Plan a constant reference if this is the case for this simple identifier.
if identRef.Value != nil {
return NewConstValue(id, identRef.Value), nil
}
// Check to see whether the type map indicates this is a type name. All types should be
// registered with the provider.
cType := p.typeMap[id]
if cType.Kind() == types.TypeKind {
cVal, found := p.provider.FindIdent(identRef.Name)
if !found {
return nil, fmt.Errorf("reference to undefined type: %s", identRef.Name)
}
return NewConstValue(id, cVal), nil
}
// Otherwise, return the attribute for the resolved identifier name.
return &evalAttr{
adapter: p.adapter,
attr: p.attrFactory.AbsoluteAttribute(id, identRef.Name),
}, nil
}
// planSelect creates an Interpretable with either:
//
// a) selects a field from a map or proto.
// b) creates a field presence test for a select within a has() macro.
// c) resolves the select expression to a namespaced identifier.
func (p *planner) planSelect(expr ast.Expr) (Interpretable, error) {
// If the Select id appears in the reference map from the CheckedExpr proto then it is either
// a namespaced identifier or enum value.
if identRef, found := p.refMap[expr.ID()]; found {
return p.planCheckedIdent(expr.ID(), identRef)
}
sel := expr.AsSelect()
// Plan the operand evaluation.
op, err := p.plan(sel.Operand())
if err != nil {
return nil, err
}
opType := p.typeMap[sel.Operand().ID()]
// If the Select was marked TestOnly, this is a presence test.
//
// Note: presence tests are defined for structured (e.g. proto) and dynamic values (map, json)
// as follows:
// - True if the object field has a non-default value, e.g. obj.str != ""
// - True if the dynamic value has the field defined, e.g. key in map
//
// However, presence tests are not defined for qualified identifier names with primitive types.
// If a string named 'a.b.c' is declared in the environment and referenced within `has(a.b.c)`,
// it is not clear whether has should error or follow the convention defined for structured
// values.
// Establish the attribute reference.
attr, isAttr := op.(InterpretableAttribute)
if !isAttr {
attr, err = p.relativeAttr(op.ID(), op, false)
if err != nil {
return nil, err
}
}
// Build a qualifier for the attribute.
qual, err := p.attrFactory.NewQualifier(opType, expr.ID(), sel.FieldName(), false)
if err != nil {
return nil, err
}
// Modify the attribute to be test-only.
if sel.IsTestOnly() {
attr = &evalTestOnly{
id: expr.ID(),
InterpretableAttribute: attr,
}
}
// Append the qualifier on the attribute.
_, err = attr.AddQualifier(qual)
return attr, err
}
// planCall creates a callable Interpretable while specializing for common functions and invocation
// patterns. Specifically, conditional operators &&, ||, ?:, and (in)equality functions result in
// optimized Interpretable values.
func (p *planner) planCall(expr ast.Expr) (Interpretable, error) {
call := expr.AsCall()
target, fnName, oName := p.resolveFunction(expr)
argCount := len(call.Args())
var offset int
if target != nil {
argCount++
offset++
}
args := make([]Interpretable, argCount)
if target != nil {
arg, err := p.plan(target)
if err != nil {
return nil, err
}
args[0] = arg
}
for i, argExpr := range call.Args() {
arg, err := p.plan(argExpr)
if err != nil {
return nil, err
}
args[i+offset] = arg
}
// Generate specialized Interpretable operators by function name if possible.
switch fnName {
case operators.LogicalAnd:
return p.planCallLogicalAnd(expr, args)
case operators.LogicalOr:
return p.planCallLogicalOr(expr, args)
case operators.Conditional:
return p.planCallConditional(expr, args)
case operators.Equals:
return p.planCallEqual(expr, args)
case operators.NotEquals:
return p.planCallNotEqual(expr, args)
case operators.Index:
return p.planCallIndex(expr, args, false)
case operators.OptSelect, operators.OptIndex:
return p.planCallIndex(expr, args, true)
}
// Otherwise, generate Interpretable calls specialized by argument count.
// Try to find the specific function by overload id.
var fnDef *functions.Overload
if oName != "" {
fnDef, _ = p.disp.FindOverload(oName)
}
// If the overload id couldn't resolve the function, try the simple function name.
if fnDef == nil {
fnDef, _ = p.disp.FindOverload(fnName)
}
switch argCount {
case 0:
return p.planCallZero(expr, fnName, oName, fnDef)
case 1:
// If the FunctionOp has been used, then use it as it may exist for the purposes
// of dynamic dispatch within a singleton function implementation.
if fnDef != nil && fnDef.Unary == nil && fnDef.Function != nil {
return p.planCallVarArgs(expr, fnName, oName, fnDef, args)
}
return p.planCallUnary(expr, fnName, oName, fnDef, args)
case 2:
// If the FunctionOp has been used, then use it as it may exist for the purposes
// of dynamic dispatch within a singleton function implementation.
if fnDef != nil && fnDef.Binary == nil && fnDef.Function != nil {
return p.planCallVarArgs(expr, fnName, oName, fnDef, args)
}
return p.planCallBinary(expr, fnName, oName, fnDef, args)
default:
return p.planCallVarArgs(expr, fnName, oName, fnDef, args)
}
}
// planCallZero generates a zero-arity callable Interpretable.
func (p *planner) planCallZero(expr ast.Expr,
function string,
overload string,
impl *functions.Overload) (Interpretable, error) {
if impl == nil || impl.Function == nil {
return nil, fmt.Errorf("no such overload: %s()", function)
}
return &evalZeroArity{
id: expr.ID(),
function: function,
overload: overload,
impl: impl.Function,
}, nil
}
// planCallUnary generates a unary callable Interpretable.
func (p *planner) planCallUnary(expr ast.Expr,
function string,
overload string,
impl *functions.Overload,
args []Interpretable) (Interpretable, error) {
var fn functions.UnaryOp
var trait int
var nonStrict bool
if impl != nil {
if impl.Unary == nil {
return nil, fmt.Errorf("no such overload: %s(arg)", function)
}
fn = impl.Unary
trait = impl.OperandTrait
nonStrict = impl.NonStrict
}
return &evalUnary{
id: expr.ID(),
function: function,
overload: overload,
arg: args[0],
trait: trait,
impl: fn,
nonStrict: nonStrict,
}, nil
}
// planCallBinary generates a binary callable Interpretable.
func (p *planner) planCallBinary(expr ast.Expr,
function string,
overload string,
impl *functions.Overload,
args []Interpretable) (Interpretable, error) {
var fn functions.BinaryOp
var trait int
var nonStrict bool
if impl != nil {
if impl.Binary == nil {
return nil, fmt.Errorf("no such overload: %s(lhs, rhs)", function)
}
fn = impl.Binary
trait = impl.OperandTrait
nonStrict = impl.NonStrict
}
return &evalBinary{
id: expr.ID(),
function: function,
overload: overload,
lhs: args[0],
rhs: args[1],
trait: trait,
impl: fn,
nonStrict: nonStrict,
}, nil
}
// planCallVarArgs generates a variable argument callable Interpretable.
func (p *planner) planCallVarArgs(expr ast.Expr,
function string,
overload string,
impl *functions.Overload,
args []Interpretable) (Interpretable, error) {
var fn functions.FunctionOp
var trait int
var nonStrict bool
if impl != nil {
if impl.Function == nil {
return nil, fmt.Errorf("no such overload: %s(...)", function)
}
fn = impl.Function
trait = impl.OperandTrait
nonStrict = impl.NonStrict
}
return &evalVarArgs{
id: expr.ID(),
function: function,
overload: overload,
args: args,
trait: trait,
impl: fn,
nonStrict: nonStrict,
}, nil
}
// planCallEqual generates an equals (==) Interpretable.
func (p *planner) planCallEqual(expr ast.Expr, args []Interpretable) (Interpretable, error) {
return &evalEq{
id: expr.ID(),
lhs: args[0],
rhs: args[1],
}, nil
}
// planCallNotEqual generates a not equals (!=) Interpretable.
func (p *planner) planCallNotEqual(expr ast.Expr, args []Interpretable) (Interpretable, error) {
return &evalNe{
id: expr.ID(),
lhs: args[0],
rhs: args[1],
}, nil
}
// planCallLogicalAnd generates a logical and (&&) Interpretable.
func (p *planner) planCallLogicalAnd(expr ast.Expr, args []Interpretable) (Interpretable, error) {
return &evalAnd{
id: expr.ID(),
terms: args,
}, nil
}
// planCallLogicalOr generates a logical or (||) Interpretable.
func (p *planner) planCallLogicalOr(expr ast.Expr, args []Interpretable) (Interpretable, error) {
return &evalOr{
id: expr.ID(),
terms: args,
}, nil
}
// planCallConditional generates a conditional / ternary (c ? t : f) Interpretable.
func (p *planner) planCallConditional(expr ast.Expr, args []Interpretable) (Interpretable, error) {
cond := args[0]
t := args[1]
var tAttr Attribute
truthyAttr, isTruthyAttr := t.(InterpretableAttribute)
if isTruthyAttr {
tAttr = truthyAttr.Attr()
} else {
tAttr = p.attrFactory.RelativeAttribute(t.ID(), t)
}
f := args[2]
var fAttr Attribute
falsyAttr, isFalsyAttr := f.(InterpretableAttribute)
if isFalsyAttr {
fAttr = falsyAttr.Attr()
} else {
fAttr = p.attrFactory.RelativeAttribute(f.ID(), f)
}
return &evalAttr{
adapter: p.adapter,
attr: p.attrFactory.ConditionalAttribute(expr.ID(), cond, tAttr, fAttr),
}, nil
}
// planCallIndex either extends an attribute with the argument to the index operation, or creates
// a relative attribute based on the return of a function call or operation.
func (p *planner) planCallIndex(expr ast.Expr, args []Interpretable, optional bool) (Interpretable, error) {
op := args[0]
ind := args[1]
opType := p.typeMap[op.ID()]
// Establish the attribute reference.
var err error
attr, isAttr := op.(InterpretableAttribute)
if !isAttr {
attr, err = p.relativeAttr(op.ID(), op, false)
if err != nil {
return nil, err
}
}
// Construct the qualifier type.
var qual Qualifier
switch ind := ind.(type) {
case InterpretableConst:
qual, err = p.attrFactory.NewQualifier(opType, expr.ID(), ind.Value(), optional)
case InterpretableAttribute:
qual, err = p.attrFactory.NewQualifier(opType, expr.ID(), ind, optional)
default:
qual, err = p.relativeAttr(expr.ID(), ind, optional)
}
if err != nil {
return nil, err
}
// Add the qualifier to the attribute
_, err = attr.AddQualifier(qual)
return attr, err
}
// planCreateList generates a list construction Interpretable.
func (p *planner) planCreateList(expr ast.Expr) (Interpretable, error) {
list := expr.AsList()
optionalIndices := list.OptionalIndices()
elements := list.Elements()
optionals := make([]bool, len(elements))
for _, index := range optionalIndices {
if index < 0 || index >= int32(len(elements)) {
return nil, fmt.Errorf("optional index %d out of element bounds [0, %d]", index, len(elements))
}
optionals[index] = true
}
elems := make([]Interpretable, len(elements))
for i, elem := range elements {
elemVal, err := p.plan(elem)
if err != nil {
return nil, err
}
elems[i] = elemVal
}
return &evalList{
id: expr.ID(),
elems: elems,
optionals: optionals,
hasOptionals: len(optionalIndices) != 0,
adapter: p.adapter,
}, nil
}
// planCreateStruct generates a map or object construction Interpretable.
func (p *planner) planCreateMap(expr ast.Expr) (Interpretable, error) {
m := expr.AsMap()
entries := m.Entries()
optionals := make([]bool, len(entries))
keys := make([]Interpretable, len(entries))
vals := make([]Interpretable, len(entries))
hasOptionals := false
for i, e := range entries {
entry := e.AsMapEntry()
keyVal, err := p.plan(entry.Key())
if err != nil {
return nil, err
}
keys[i] = keyVal
valVal, err := p.plan(entry.Value())
if err != nil {
return nil, err
}
vals[i] = valVal
optionals[i] = entry.IsOptional()
hasOptionals = hasOptionals || entry.IsOptional()
}
return &evalMap{
id: expr.ID(),
keys: keys,
vals: vals,
optionals: optionals,
hasOptionals: hasOptionals,
adapter: p.adapter,
}, nil
}
// planCreateObj generates an object construction Interpretable.
func (p *planner) planCreateStruct(expr ast.Expr) (Interpretable, error) {
obj := expr.AsStruct()
typeName, defined := p.resolveTypeName(obj.TypeName())
if !defined {
return nil, fmt.Errorf("unknown type: %s", obj.TypeName())
}
objFields := obj.Fields()
optionals := make([]bool, len(objFields))
fields := make([]string, len(objFields))
vals := make([]Interpretable, len(objFields))
hasOptionals := false
for i, f := range objFields {
field := f.AsStructField()
fields[i] = field.Name()
val, err := p.plan(field.Value())
if err != nil {
return nil, err
}
vals[i] = val
optionals[i] = field.IsOptional()
hasOptionals = hasOptionals || field.IsOptional()
}
return &evalObj{
id: expr.ID(),
typeName: typeName,
fields: fields,
vals: vals,
optionals: optionals,
hasOptionals: hasOptionals,
provider: p.provider,
}, nil
}
// planComprehension generates an Interpretable fold operation.
func (p *planner) planComprehension(expr ast.Expr) (Interpretable, error) {
fold := expr.AsComprehension()
accu, err := p.plan(fold.AccuInit())
if err != nil {
return nil, err
}
iterRange, err := p.plan(fold.IterRange())
if err != nil {
return nil, err
}
cond, err := p.plan(fold.LoopCondition())
if err != nil {
return nil, err
}
step, err := p.plan(fold.LoopStep())
if err != nil {
return nil, err
}
result, err := p.plan(fold.Result())
if err != nil {
return nil, err
}
return &evalFold{
id: expr.ID(),
accuVar: fold.AccuVar(),
accu: accu,
iterVar: fold.IterVar(),
iterVar2: fold.IterVar2(),
iterRange: iterRange,
cond: cond,
step: step,
result: result,
adapter: p.adapter,
}, nil
}
// planConst generates a constant valued Interpretable.
func (p *planner) planConst(expr ast.Expr) (Interpretable, error) {
return NewConstValue(expr.ID(), expr.AsLiteral()), nil
}
// resolveTypeName takes a qualified string constructed at parse time, applies the proto
// namespace resolution rules to it in a scan over possible matching types in the TypeProvider.
func (p *planner) resolveTypeName(typeName string) (string, bool) {
for _, qualifiedTypeName := range p.container.ResolveCandidateNames(typeName) {
if _, found := p.provider.FindStructType(qualifiedTypeName); found {
return qualifiedTypeName, true
}
}
return "", false
}
// resolveFunction determines the call target, function name, and overload name from a given Expr
// value.
//
// The resolveFunction resolves ambiguities where a function may either be a receiver-style
// invocation or a qualified global function name.
// - The target expression may only consist of ident and select expressions.
// - The function is declared in the environment using its fully-qualified name.
// - The fully-qualified function name matches the string serialized target value.
func (p *planner) resolveFunction(expr ast.Expr) (ast.Expr, string, string) {
// Note: similar logic exists within the `checker/checker.go`. If making changes here
// please consider the impact on checker.go and consolidate implementations or mirror code
// as appropriate.
call := expr.AsCall()
var target ast.Expr = nil
if call.IsMemberFunction() {
target = call.Target()
}
fnName := call.FunctionName()
// Checked expressions always have a reference map entry, and _should_ have the fully qualified
// function name as the fnName value.
oRef, hasOverload := p.refMap[expr.ID()]
if hasOverload {
if len(oRef.OverloadIDs) == 1 {
return target, fnName, oRef.OverloadIDs[0]
}
// Note, this namespaced function name will not appear as a fully qualified name in ASTs
// built and stored before cel-go v0.5.0; however, this functionality did not work at all
// before the v0.5.0 release.
return target, fnName, ""
}
// Parse-only expressions need to handle the same logic as is normally performed at check time,
// but with potentially much less information. The only reliable source of information about
// which functions are configured is the dispatcher.
if target == nil {
// If the user has a parse-only expression, then it should have been configured as such in
// the interpreter dispatcher as it may have been omitted from the checker environment.
for _, qualifiedName := range p.container.ResolveCandidateNames(fnName) {
_, found := p.disp.FindOverload(qualifiedName)
if found {
return nil, qualifiedName, ""
}
}
// It's possible that the overload was not found, but this situation is accounted for in
// the planCall phase; however, the leading dot used for denoting fully-qualified
// namespaced identifiers must be stripped, as all declarations already use fully-qualified
// names. This stripping behavior is handled automatically by the ResolveCandidateNames
// call.
return target, stripLeadingDot(fnName), ""
}
// Handle the situation where the function target actually indicates a qualified function name.
qualifiedPrefix, maybeQualified := p.toQualifiedName(target)
if maybeQualified {
maybeQualifiedName := qualifiedPrefix + "." + fnName
for _, qualifiedName := range p.container.ResolveCandidateNames(maybeQualifiedName) {
_, found := p.disp.FindOverload(qualifiedName)
if found {
// Clear the target to ensure the proper arity is used for finding the
// implementation.
return nil, qualifiedName, ""
}
}
}
// In the default case, the function is exactly as it was advertised: a receiver call on with
// an expression-based target with the given simple function name.
return target, fnName, ""
}
// relativeAttr indicates that the attribute in this case acts as a qualifier and as such needs to
// be observed to ensure that it's evaluation value is properly recorded for state tracking.
func (p *planner) relativeAttr(id int64, eval Interpretable, opt bool) (InterpretableAttribute, error) {
eAttr, ok := eval.(InterpretableAttribute)
if !ok {
eAttr = &evalAttr{
adapter: p.adapter,
attr: p.attrFactory.RelativeAttribute(id, eval),
optional: opt,
}
}
// This looks like it should either decorate the new evalAttr node, or early return the InterpretableAttribute
decAttr, err := p.decorate(eAttr, nil)
if err != nil {
return nil, err
}
eAttr, ok = decAttr.(InterpretableAttribute)
if !ok {
return nil, fmt.Errorf("invalid attribute decoration: %v(%T)", decAttr, decAttr)
}
return eAttr, nil
}
// toQualifiedName converts an expression AST into a qualified name if possible, with a boolean
// 'found' value that indicates if the conversion is successful.
func (p *planner) toQualifiedName(operand ast.Expr) (string, bool) {
// If the checker identified the expression as an attribute by the type-checker, then it can't
// possibly be part of qualified name in a namespace.
_, isAttr := p.refMap[operand.ID()]
if isAttr {
return "", false
}
// Since functions cannot be both namespaced and receiver functions, if the operand is not an
// qualified variable name, return the (possibly) qualified name given the expressions.
switch operand.Kind() {
case ast.IdentKind:
id := operand.AsIdent()
return id, true
case ast.SelectKind:
sel := operand.AsSelect()
// Test only expressions are not valid as qualified names.
if sel.IsTestOnly() {
return "", false
}
if qual, found := p.toQualifiedName(sel.Operand()); found {
return qual + "." + sel.FieldName(), true
}
}
return "", false
}
func stripLeadingDot(name string) string {
if strings.HasPrefix(name, ".") {
return name[1:]
}
return name
}
// Copyright 2018 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package interpreter
import (
"github.com/google/cel-go/common/ast"
"github.com/google/cel-go/common/operators"
"github.com/google/cel-go/common/overloads"
"github.com/google/cel-go/common/types"
"github.com/google/cel-go/common/types/ref"
"github.com/google/cel-go/common/types/traits"
)
type astPruner struct {
ast.ExprFactory
expr ast.Expr
macroCalls map[int64]ast.Expr
state EvalState
nextExprID int64
}
// TODO Consider having a separate walk of the AST that finds common
// subexpressions. This can be called before or after constant folding to find
// common subexpressions.
// PruneAst prunes the given AST based on the given EvalState and generates a new AST.
// Given AST is copied on write and a new AST is returned.
// Couple of typical use cases this interface would be:
//
// A)
// 1) Evaluate expr with some unknowns,
// 2) If result is unknown:
//
// a) PruneAst
// b) Goto 1
//
// Functional call results which are known would be effectively cached across
// iterations.
//
// B)
// 1) Compile the expression (maybe via a service and maybe after checking a
//
// compiled expression does not exists in local cache)
//
// 2) Prepare the environment and the interpreter. Activation might be empty.
// 3) Eval the expression. This might return unknown or error or a concrete
//
// value.
//
// 4) PruneAst
// 4) Maybe cache the expression
// This is effectively constant folding the expression. How the environment is
// prepared in step 2 is flexible. For example, If the caller caches the
// compiled and constant folded expressions, but is not willing to constant
// fold(and thus cache results of) some external calls, then they can prepare
// the overloads accordingly.
func PruneAst(expr ast.Expr, macroCalls map[int64]ast.Expr, state EvalState) *ast.AST {
pruneState := NewEvalState()
for _, id := range state.IDs() {
v, _ := state.Value(id)
pruneState.SetValue(id, v)
}
pruner := &astPruner{
ExprFactory: ast.NewExprFactory(),
expr: expr,
macroCalls: macroCalls,
state: pruneState,
nextExprID: getMaxID(expr)}
newExpr, _ := pruner.maybePrune(expr)
newInfo := ast.NewSourceInfo(nil)
for id, call := range pruner.macroCalls {
newInfo.SetMacroCall(id, call)
}
return ast.NewAST(newExpr, newInfo)
}
func (p *astPruner) maybeCreateLiteral(id int64, val ref.Val) (ast.Expr, bool) {
switch v := val.(type) {
case types.Bool, types.Bytes, types.Double, types.Int, types.Null, types.String, types.Uint, *types.Optional:
p.state.SetValue(id, val)
return p.NewLiteral(id, val), true
case types.Duration:
p.state.SetValue(id, val)
durationString := v.ConvertToType(types.StringType).(types.String)
return p.NewCall(id, overloads.TypeConvertDuration, p.NewLiteral(p.nextID(), durationString)), true
case types.Timestamp:
timestampString := v.ConvertToType(types.StringType).(types.String)
return p.NewCall(id, overloads.TypeConvertTimestamp, p.NewLiteral(p.nextID(), timestampString)), true
}
// Attempt to build a list literal.
if list, isList := val.(traits.Lister); isList {
sz := list.Size().(types.Int)
elemExprs := make([]ast.Expr, sz)
for i := types.Int(0); i < sz; i++ {
elem := list.Get(i)
if types.IsUnknownOrError(elem) {
return nil, false
}
elemExpr, ok := p.maybeCreateLiteral(p.nextID(), elem)
if !ok {
return nil, false
}
elemExprs[i] = elemExpr
}
p.state.SetValue(id, val)
return p.NewList(id, elemExprs, []int32{}), true
}
// Create a map literal if possible.
if mp, isMap := val.(traits.Mapper); isMap {
it := mp.Iterator()
entries := make([]ast.EntryExpr, mp.Size().(types.Int))
i := 0
for it.HasNext() != types.False {
key := it.Next()
val := mp.Get(key)
if types.IsUnknownOrError(key) || types.IsUnknownOrError(val) {
return nil, false
}
keyExpr, ok := p.maybeCreateLiteral(p.nextID(), key)
if !ok {
return nil, false
}
valExpr, ok := p.maybeCreateLiteral(p.nextID(), val)
if !ok {
return nil, false
}
entry := p.NewMapEntry(p.nextID(), keyExpr, valExpr, false)
entries[i] = entry
i++
}
p.state.SetValue(id, val)
return p.NewMap(id, entries), true
}
// TODO(issues/377) To construct message literals, the type provider will need to support
// the enumeration the fields for a given message.
return nil, false
}
func (p *astPruner) maybePruneOptional(elem ast.Expr) (ast.Expr, bool) {
elemVal, found := p.value(elem.ID())
if found && elemVal.Type() == types.OptionalType {
opt := elemVal.(*types.Optional)
if !opt.HasValue() {
return nil, true
}
if newElem, pruned := p.maybeCreateLiteral(elem.ID(), opt.GetValue()); pruned {
return newElem, true
}
}
return elem, false
}
func (p *astPruner) maybePruneIn(node ast.Expr) (ast.Expr, bool) {
// elem in list
call := node.AsCall()
val, exists := p.maybeValue(call.Args()[1].ID())
if !exists {
return nil, false
}
if sz, ok := val.(traits.Sizer); ok && sz.Size() == types.IntZero {
return p.maybeCreateLiteral(node.ID(), types.False)
}
return nil, false
}
func (p *astPruner) maybePruneLogicalNot(node ast.Expr) (ast.Expr, bool) {
call := node.AsCall()
arg := call.Args()[0]
val, exists := p.maybeValue(arg.ID())
if !exists {
return nil, false
}
if b, ok := val.(types.Bool); ok {
return p.maybeCreateLiteral(node.ID(), !b)
}
return nil, false
}
func (p *astPruner) maybePruneOr(node ast.Expr) (ast.Expr, bool) {
call := node.AsCall()
// We know result is unknown, so we have at least one unknown arg
// and if one side is a known value, we know we can ignore it.
if v, exists := p.maybeValue(call.Args()[0].ID()); exists {
if v == types.True {
return p.maybeCreateLiteral(node.ID(), types.True)
}
return call.Args()[1], true
}
if v, exists := p.maybeValue(call.Args()[1].ID()); exists {
if v == types.True {
return p.maybeCreateLiteral(node.ID(), types.True)
}
return call.Args()[0], true
}
return nil, false
}
func (p *astPruner) maybePruneAnd(node ast.Expr) (ast.Expr, bool) {
call := node.AsCall()
// We know result is unknown, so we have at least one unknown arg
// and if one side is a known value, we know we can ignore it.
if v, exists := p.maybeValue(call.Args()[0].ID()); exists {
if v == types.False {
return p.maybeCreateLiteral(node.ID(), types.False)
}
return call.Args()[1], true
}
if v, exists := p.maybeValue(call.Args()[1].ID()); exists {
if v == types.False {
return p.maybeCreateLiteral(node.ID(), types.False)
}
return call.Args()[0], true
}
return nil, false
}
func (p *astPruner) maybePruneConditional(node ast.Expr) (ast.Expr, bool) {
call := node.AsCall()
cond, exists := p.maybeValue(call.Args()[0].ID())
if !exists {
return nil, false
}
if cond.Value().(bool) {
return call.Args()[1], true
}
return call.Args()[2], true
}
func (p *astPruner) maybePruneFunction(node ast.Expr) (ast.Expr, bool) {
if _, exists := p.value(node.ID()); !exists {
return nil, false
}
call := node.AsCall()
if call.FunctionName() == operators.LogicalOr {
return p.maybePruneOr(node)
}
if call.FunctionName() == operators.LogicalAnd {
return p.maybePruneAnd(node)
}
if call.FunctionName() == operators.Conditional {
return p.maybePruneConditional(node)
}
if call.FunctionName() == operators.In {
return p.maybePruneIn(node)
}
if call.FunctionName() == operators.LogicalNot {
return p.maybePruneLogicalNot(node)
}
return nil, false
}
func (p *astPruner) maybePrune(node ast.Expr) (ast.Expr, bool) {
return p.prune(node)
}
func (p *astPruner) prune(node ast.Expr) (ast.Expr, bool) {
if node == nil {
return node, false
}
val, valueExists := p.maybeValue(node.ID())
if valueExists {
if newNode, ok := p.maybeCreateLiteral(node.ID(), val); ok {
delete(p.macroCalls, node.ID())
return newNode, true
}
}
if macro, found := p.macroCalls[node.ID()]; found {
// Ensure that intermediate values for the comprehension are cleared during pruning
pruneMacroCall := node.Kind() != ast.UnspecifiedExprKind
if node.Kind() == ast.ComprehensionKind {
// Only prune cel.bind() calls since the variables of the comprehension are all
// visible to the user, so there's no chance of an incorrect value being observed
// as a result of looking at intermediate computations within a comprehension.
pruneMacroCall = isCelBindMacro(macro)
}
if pruneMacroCall {
// prune the expression in terms of the macro call instead of the expanded form when
// dealing with macro call tracking references.
if newMacro, pruned := p.prune(macro); pruned {
p.macroCalls[node.ID()] = newMacro
}
} else {
// Otherwise just prune the macro target in keeping with the pruning behavior of the
// comprehensions later in the call graph.
macroCall := macro.AsCall()
if macroCall.Target() != nil {
if newTarget, pruned := p.prune(macroCall.Target()); pruned {
macro = p.NewMemberCall(macro.ID(), macroCall.FunctionName(), newTarget, macroCall.Args()...)
p.macroCalls[node.ID()] = macro
}
}
}
}
// We have either an unknown/error value, or something we don't want to
// transform, or expression was not evaluated. If possible, drill down
// more.
switch node.Kind() {
case ast.SelectKind:
sel := node.AsSelect()
if operand, isPruned := p.maybePrune(sel.Operand()); isPruned {
if sel.IsTestOnly() {
return p.NewPresenceTest(node.ID(), operand, sel.FieldName()), true
}
return p.NewSelect(node.ID(), operand, sel.FieldName()), true
}
case ast.CallKind:
argsPruned := false
call := node.AsCall()
args := call.Args()
newArgs := make([]ast.Expr, len(args))
for i, a := range args {
newArgs[i] = a
if arg, isPruned := p.maybePrune(a); isPruned {
argsPruned = true
newArgs[i] = arg
}
}
if !call.IsMemberFunction() {
newCall := p.NewCall(node.ID(), call.FunctionName(), newArgs...)
if prunedCall, isPruned := p.maybePruneFunction(newCall); isPruned {
return prunedCall, true
}
return newCall, argsPruned
}
newTarget := call.Target()
targetPruned := false
if prunedTarget, isPruned := p.maybePrune(call.Target()); isPruned {
targetPruned = true
newTarget = prunedTarget
}
newCall := p.NewMemberCall(node.ID(), call.FunctionName(), newTarget, newArgs...)
if prunedCall, isPruned := p.maybePruneFunction(newCall); isPruned {
return prunedCall, true
}
return newCall, targetPruned || argsPruned
case ast.ListKind:
l := node.AsList()
elems := l.Elements()
optIndices := l.OptionalIndices()
optIndexMap := map[int32]bool{}
for _, i := range optIndices {
optIndexMap[i] = true
}
newOptIndexMap := make(map[int32]bool, len(optIndexMap))
newElems := make([]ast.Expr, 0, len(elems))
var listPruned bool
prunedIdx := 0
for i, elem := range elems {
_, isOpt := optIndexMap[int32(i)]
if isOpt {
newElem, pruned := p.maybePruneOptional(elem)
if pruned {
listPruned = true
if newElem != nil {
newElems = append(newElems, newElem)
prunedIdx++
}
continue
}
newOptIndexMap[int32(prunedIdx)] = true
}
if newElem, prunedElem := p.maybePrune(elem); prunedElem {
newElems = append(newElems, newElem)
listPruned = true
} else {
newElems = append(newElems, elem)
}
prunedIdx++
}
optIndices = make([]int32, len(newOptIndexMap))
idx := 0
for i := range newOptIndexMap {
optIndices[idx] = i
idx++
}
if listPruned {
return p.NewList(node.ID(), newElems, optIndices), true
}
case ast.MapKind:
var mapPruned bool
m := node.AsMap()
entries := m.Entries()
newEntries := make([]ast.EntryExpr, len(entries))
for i, entry := range entries {
newEntries[i] = entry
e := entry.AsMapEntry()
newKey, keyPruned := p.maybePrune(e.Key())
newValue, valuePruned := p.maybePrune(e.Value())
if !keyPruned && !valuePruned {
continue
}
mapPruned = true
newEntry := p.NewMapEntry(entry.ID(), newKey, newValue, e.IsOptional())
newEntries[i] = newEntry
}
if mapPruned {
return p.NewMap(node.ID(), newEntries), true
}
case ast.StructKind:
var structPruned bool
obj := node.AsStruct()
fields := obj.Fields()
newFields := make([]ast.EntryExpr, len(fields))
for i, field := range fields {
newFields[i] = field
f := field.AsStructField()
newValue, prunedValue := p.maybePrune(f.Value())
if !prunedValue {
continue
}
structPruned = true
newEntry := p.NewStructField(field.ID(), f.Name(), newValue, f.IsOptional())
newFields[i] = newEntry
}
if structPruned {
return p.NewStruct(node.ID(), obj.TypeName(), newFields), true
}
case ast.ComprehensionKind:
compre := node.AsComprehension()
// Only the range of the comprehension is pruned since the state tracking only records
// the last iteration of the comprehension and not each step in the evaluation which
// means that the any residuals computed in between might be inaccurate.
if newRange, pruned := p.maybePrune(compre.IterRange()); pruned {
if compre.HasIterVar2() {
return p.NewComprehensionTwoVar(
node.ID(),
newRange,
compre.IterVar(),
compre.IterVar2(),
compre.AccuVar(),
compre.AccuInit(),
compre.LoopCondition(),
compre.LoopStep(),
compre.Result(),
), true
}
return p.NewComprehension(
node.ID(),
newRange,
compre.IterVar(),
compre.AccuVar(),
compre.AccuInit(),
compre.LoopCondition(),
compre.LoopStep(),
compre.Result(),
), true
}
}
return node, false
}
func (p *astPruner) value(id int64) (ref.Val, bool) {
val, found := p.state.Value(id)
return val, (found && val != nil)
}
func (p *astPruner) maybeValue(id int64) (ref.Val, bool) {
val, found := p.value(id)
if !found || types.IsUnknownOrError(val) {
return nil, false
}
return val, true
}
func (p *astPruner) nextID() int64 {
next := p.nextExprID
p.nextExprID++
return next
}
type astVisitor struct {
// visitEntry is called on every expr node, including those within a map/struct entry.
visitExpr func(expr ast.Expr)
// visitEntry is called before entering the key, value of a map/struct entry.
visitEntry func(entry ast.EntryExpr)
}
func getMaxID(expr ast.Expr) int64 {
maxID := int64(1)
visit(expr, maxIDVisitor(&maxID))
return maxID
}
func maxIDVisitor(maxID *int64) astVisitor {
return astVisitor{
visitExpr: func(e ast.Expr) {
if e.ID() >= *maxID {
*maxID = e.ID() + 1
}
},
visitEntry: func(e ast.EntryExpr) {
if e.ID() >= *maxID {
*maxID = e.ID() + 1
}
},
}
}
func visit(expr ast.Expr, visitor astVisitor) {
exprs := []ast.Expr{expr}
for len(exprs) != 0 {
e := exprs[0]
if visitor.visitExpr != nil {
visitor.visitExpr(e)
}
exprs = exprs[1:]
switch e.Kind() {
case ast.SelectKind:
exprs = append(exprs, e.AsSelect().Operand())
case ast.CallKind:
call := e.AsCall()
if call.Target() != nil {
exprs = append(exprs, call.Target())
}
exprs = append(exprs, call.Args()...)
case ast.ComprehensionKind:
compre := e.AsComprehension()
exprs = append(exprs,
compre.IterRange(),
compre.AccuInit(),
compre.LoopCondition(),
compre.LoopStep(),
compre.Result())
case ast.ListKind:
list := e.AsList()
exprs = append(exprs, list.Elements()...)
case ast.MapKind:
for _, entry := range e.AsMap().Entries() {
e := entry.AsMapEntry()
if visitor.visitEntry != nil {
visitor.visitEntry(entry)
}
exprs = append(exprs, e.Key())
exprs = append(exprs, e.Value())
}
case ast.StructKind:
for _, entry := range e.AsStruct().Fields() {
f := entry.AsStructField()
if visitor.visitEntry != nil {
visitor.visitEntry(entry)
}
exprs = append(exprs, f.Value())
}
}
}
}
func isCelBindMacro(macro ast.Expr) bool {
if macro.Kind() != ast.CallKind {
return false
}
macroCall := macro.AsCall()
target := macroCall.Target()
return macroCall.FunctionName() == "bind" &&
macroCall.IsMemberFunction() &&
target.Kind() == ast.IdentKind &&
target.AsIdent() == "cel"
}
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package interpreter
import (
"errors"
"math"
"github.com/google/cel-go/common"
"github.com/google/cel-go/common/overloads"
"github.com/google/cel-go/common/types"
"github.com/google/cel-go/common/types/ref"
"github.com/google/cel-go/common/types/traits"
)
// WARNING: Any changes to cost calculations in this file require a corresponding change in checker/cost.go
// ActualCostEstimator provides function call cost estimations at runtime
// CallCost returns an estimated cost for the function overload invocation with the given args, or nil if it has no
// estimate to provide. CEL attempts to provide reasonable estimates for its standard function library, so CallCost
// should typically not need to provide an estimate for CELs standard function.
type ActualCostEstimator interface {
CallCost(function, overloadID string, args []ref.Val, result ref.Val) *uint64
}
// costTrackPlanOption modifies the cost tracking factory associatied with the CostObserver
type costTrackPlanOption func(*costTrackerFactory) *costTrackerFactory
// CostTrackerFactory configures the factory method to generate a new cost-tracker per-evaluation.
func CostTrackerFactory(factory func() (*CostTracker, error)) costTrackPlanOption {
return func(fac *costTrackerFactory) *costTrackerFactory {
fac.factory = factory
return fac
}
}
// CostObserver provides an observer that tracks runtime cost.
func CostObserver(opts ...costTrackPlanOption) PlannerOption {
ct := &costTrackerFactory{}
for _, o := range opts {
ct = o(ct)
}
return func(p *planner) (*planner, error) {
if ct.factory == nil {
return nil, errors.New("cost tracker factory not configured")
}
p.observers = append(p.observers, ct)
p.decorators = append(p.decorators, decObserveEval(ct.Observe))
return p, nil
}
}
// costTrackerConverter identifies an object which is convertible to a CostTracker instance.
type costTrackerConverter interface {
asCostTracker() *CostTracker
}
// costTrackActivation hides state in the Activation in a manner not accessible to expressions.
type costTrackActivation struct {
vars Activation
costTracker *CostTracker
}
// ResolveName proxies variable lookups to the backing activation.
func (cta costTrackActivation) ResolveName(name string) (any, bool) {
return cta.vars.ResolveName(name)
}
// Parent proxies parent lookups to the backing activation.
func (cta costTrackActivation) Parent() Activation {
return cta.vars
}
// AsPartialActivation supports conversion to a partial activation in order to detect unknown attributes.
func (cta costTrackActivation) AsPartialActivation() (PartialActivation, bool) {
return AsPartialActivation(cta.vars)
}
// asCostTracker implements the costTrackerConverter method.
func (cta costTrackActivation) asCostTracker() *CostTracker {
return cta.costTracker
}
// asCostTracker walks the Activation hierarchy and returns the first cost tracker found, if present.
func asCostTracker(vars Activation) (*CostTracker, bool) {
if conv, ok := vars.(costTrackerConverter); ok {
return conv.asCostTracker(), true
}
if vars.Parent() != nil {
return asCostTracker(vars.Parent())
}
return nil, false
}
// costTrackerFactory holds a factory for producing new CostTracker instances on each Eval call.
type costTrackerFactory struct {
factory func() (*CostTracker, error)
}
// InitState produces a CostTracker and bundles it into an Activation in a way which is not visible
// to expression evaluation.
func (ct *costTrackerFactory) InitState(vars Activation) (Activation, error) {
tracker, err := ct.factory()
if err != nil {
return nil, err
}
return costTrackActivation{vars: vars, costTracker: tracker}, nil
}
// GetState extracts the CostTracker from the Activation.
func (ct *costTrackerFactory) GetState(vars Activation) any {
if tracker, found := asCostTracker(vars); found {
return tracker
}
return nil
}
// Observe computes the incremental cost of each step and records it into the CostTracker associated
// with the evaluation.
func (ct *costTrackerFactory) Observe(vars Activation, id int64, programStep any, val ref.Val) {
tracker, found := asCostTracker(vars)
if !found {
return
}
switch t := programStep.(type) {
case ConstantQualifier:
// TODO: Push identifiers on to the stack before observing constant qualifiers that apply to them
// and enable the below pop. Once enabled this can case can be collapsed into the Qualifier case.
tracker.cost++
case InterpretableConst:
// zero cost
case InterpretableAttribute:
switch a := t.Attr().(type) {
case *conditionalAttribute:
// Ternary has no direct cost. All cost is from the conditional and the true/false branch expressions.
tracker.stack.drop(a.falsy.ID(), a.truthy.ID(), a.expr.ID())
default:
tracker.stack.drop(t.Attr().ID())
tracker.cost += common.SelectAndIdentCost
}
if !tracker.presenceTestHasCost {
if _, isTestOnly := programStep.(*evalTestOnly); isTestOnly {
tracker.cost -= common.SelectAndIdentCost
}
}
case *evalExhaustiveConditional:
// Ternary has no direct cost. All cost is from the conditional and the true/false branch expressions.
tracker.stack.drop(t.attr.falsy.ID(), t.attr.truthy.ID(), t.attr.expr.ID())
// While the field names are identical, the boolean operation eval structs do not share an interface and so
// must be handled individually.
case *evalOr:
for _, term := range t.terms {
tracker.stack.drop(term.ID())
}
case *evalAnd:
for _, term := range t.terms {
tracker.stack.drop(term.ID())
}
case *evalExhaustiveOr:
for _, term := range t.terms {
tracker.stack.drop(term.ID())
}
case *evalExhaustiveAnd:
for _, term := range t.terms {
tracker.stack.drop(term.ID())
}
case *evalFold:
tracker.stack.drop(t.iterRange.ID())
case Qualifier:
tracker.cost++
case InterpretableCall:
if argVals, ok := tracker.stack.dropArgs(t.Args()); ok {
tracker.cost += tracker.costCall(t, argVals, val)
}
case InterpretableConstructor:
tracker.stack.dropArgs(t.InitVals())
switch t.Type() {
case types.ListType:
tracker.cost += common.ListCreateBaseCost
case types.MapType:
tracker.cost += common.MapCreateBaseCost
default:
tracker.cost += common.StructCreateBaseCost
}
}
tracker.stack.push(val, id)
if tracker.Limit != nil && tracker.cost > *tracker.Limit {
panic(EvalCancelledError{Cause: CostLimitExceeded, Message: "operation cancelled: actual cost limit exceeded"})
}
}
// CostTrackerOption configures the behavior of CostTracker objects.
type CostTrackerOption func(*CostTracker) error
// CostTrackerLimit sets the runtime limit on the evaluation cost during execution and will terminate the expression
// evaluation if the limit is exceeded.
func CostTrackerLimit(limit uint64) CostTrackerOption {
return func(tracker *CostTracker) error {
tracker.Limit = &limit
return nil
}
}
// PresenceTestHasCost determines whether presence testing has a cost of one or zero.
// Defaults to presence test has a cost of one.
func PresenceTestHasCost(hasCost bool) CostTrackerOption {
return func(tracker *CostTracker) error {
tracker.presenceTestHasCost = hasCost
return nil
}
}
// NewCostTracker creates a new CostTracker with a given estimator and a set of functional CostTrackerOption values.
func NewCostTracker(estimator ActualCostEstimator, opts ...CostTrackerOption) (*CostTracker, error) {
tracker := &CostTracker{
Estimator: estimator,
overloadTrackers: map[string]FunctionTracker{},
presenceTestHasCost: true,
}
for _, opt := range opts {
err := opt(tracker)
if err != nil {
return nil, err
}
}
return tracker, nil
}
// OverloadCostTracker binds an overload ID to a runtime FunctionTracker implementation.
//
// OverloadCostTracker instances augment or override ActualCostEstimator decisions, allowing for versioned and/or
// optional cost tracking changes.
func OverloadCostTracker(overloadID string, fnTracker FunctionTracker) CostTrackerOption {
return func(tracker *CostTracker) error {
tracker.overloadTrackers[overloadID] = fnTracker
return nil
}
}
// FunctionTracker computes the actual cost of evaluating the functions with the given arguments and result.
type FunctionTracker func(args []ref.Val, result ref.Val) *uint64
// CostTracker represents the information needed for tracking runtime cost.
type CostTracker struct {
Estimator ActualCostEstimator
overloadTrackers map[string]FunctionTracker
Limit *uint64
presenceTestHasCost bool
cost uint64
stack refValStack
}
// ActualCost returns the runtime cost
func (c *CostTracker) ActualCost() uint64 {
return c.cost
}
func (c *CostTracker) costCall(call InterpretableCall, args []ref.Val, result ref.Val) uint64 {
var cost uint64
if len(c.overloadTrackers) != 0 {
if tracker, found := c.overloadTrackers[call.OverloadID()]; found {
callCost := tracker(args, result)
if callCost != nil {
cost += *callCost
return cost
}
}
}
if c.Estimator != nil {
callCost := c.Estimator.CallCost(call.Function(), call.OverloadID(), args, result)
if callCost != nil {
cost += *callCost
return cost
}
}
// if user didn't specify, the default way of calculating runtime cost would be used.
// if user has their own implementation of ActualCostEstimator, make sure to cover the mapping between overloadId and cost calculation
switch call.OverloadID() {
// O(n) functions
case overloads.StartsWithString, overloads.EndsWithString, overloads.StringToBytes, overloads.BytesToString, overloads.ExtQuoteString, overloads.ExtFormatString:
cost += uint64(math.Ceil(float64(actualSize(args[0])) * common.StringTraversalCostFactor))
case overloads.InList:
// If a list is composed entirely of constant values this is O(1), but we don't account for that here.
// We just assume all list containment checks are O(n).
cost += actualSize(args[1])
// O(min(m, n)) functions
case overloads.LessString, overloads.GreaterString, overloads.LessEqualsString, overloads.GreaterEqualsString,
overloads.LessBytes, overloads.GreaterBytes, overloads.LessEqualsBytes, overloads.GreaterEqualsBytes,
overloads.Equals, overloads.NotEquals:
// When we check the equality of 2 scalar values (e.g. 2 integers, 2 floating-point numbers, 2 booleans etc.),
// the CostTracker.ActualSize() function by definition returns 1 for each operand, resulting in an overall cost
// of 1.
lhsSize := actualSize(args[0])
rhsSize := actualSize(args[1])
minSize := lhsSize
if rhsSize < minSize {
minSize = rhsSize
}
cost += uint64(math.Ceil(float64(minSize) * common.StringTraversalCostFactor))
// O(m+n) functions
case overloads.AddString, overloads.AddBytes:
// In the worst case scenario, we would need to reallocate a new backing store and copy both operands over.
cost += uint64(math.Ceil(float64(actualSize(args[0])+actualSize(args[1])) * common.StringTraversalCostFactor))
// O(nm) functions
case overloads.MatchesString:
// https://swtch.com/~rsc/regexp/regexp1.html applies to RE2 implementation supported by CEL
// Add one to string length for purposes of cost calculation to prevent product of string and regex to be 0
// in case where string is empty but regex is still expensive.
strCost := uint64(math.Ceil((1.0 + float64(actualSize(args[0]))) * common.StringTraversalCostFactor))
// We don't know how many expressions are in the regex, just the string length (a huge
// improvement here would be to somehow get a count the number of expressions in the regex or
// how many states are in the regex state machine and use that to measure regex cost).
// For now, we're making a guess that each expression in a regex is typically at least 4 chars
// in length.
regexCost := uint64(math.Ceil(float64(actualSize(args[1])) * common.RegexStringLengthCostFactor))
cost += strCost * regexCost
case overloads.ContainsString:
strCost := uint64(math.Ceil(float64(actualSize(args[0])) * common.StringTraversalCostFactor))
substrCost := uint64(math.Ceil(float64(actualSize(args[1])) * common.StringTraversalCostFactor))
cost += strCost * substrCost
default:
// The following operations are assumed to have O(1) complexity.
// - AddList due to the implementation. Index lookup can be O(c) the
// number of concatenated lists, but we don't track that is cost calculations.
// - Conversions, since none perform a traversal of a type of unbound length.
// - Computing the size of strings, byte sequences, lists and maps.
// - Logical operations and all operators on fixed width scalars (comparisons, equality)
// - Any functions that don't have a declared cost either here or in provided ActualCostEstimator.
cost++
}
return cost
}
// actualSize returns the size of the value for all traits.Sizer values, a fixed size for all proto-based
// objects, and a size of 1 for all other value types.
func actualSize(value ref.Val) uint64 {
if sz, ok := value.(traits.Sizer); ok {
return uint64(sz.Size().(types.Int))
}
if opt, ok := value.(*types.Optional); ok && opt.HasValue() {
return actualSize(opt.GetValue())
}
return 1
}
type stackVal struct {
Val ref.Val
ID int64
}
// refValStack keeps track of values of the stack for cost calculation purposes
type refValStack []stackVal
func (s *refValStack) push(val ref.Val, id int64) {
value := stackVal{Val: val, ID: id}
*s = append(*s, value)
}
// TODO: Allowing drop and dropArgs to remove stack items above the IDs they are provided is a workaround. drop and dropArgs
// should find and remove only the stack items matching the provided IDs once all attributes are properly pushed and popped from stack.
// drop searches the stack for each ID and removes the ID and all stack items above it.
// If none of the IDs are found, the stack is not modified.
// WARNING: It is possible for multiple expressions with the same ID to exist (due to how macros are implemented) so it's
// possible that a dropped ID will remain on the stack. They should be removed when IDs on the stack are popped.
func (s *refValStack) drop(ids ...int64) {
for _, id := range ids {
for idx := len(*s) - 1; idx >= 0; idx-- {
if (*s)[idx].ID == id {
*s = (*s)[:idx]
break
}
}
}
}
// dropArgs searches the stack for all the args by their IDs, accumulates their associated ref.Vals and drops any
// stack items above any of the arg IDs. If any of the IDs are not found the stack, false is returned.
// Args are assumed to be found in the stack in reverse order, i.e. the last arg is expected to be found highest in
// the stack.
// WARNING: It is possible for multiple expressions with the same ID to exist (due to how macros are implemented) so it's
// possible that a dropped ID will remain on the stack. They should be removed when IDs on the stack are popped.
func (s *refValStack) dropArgs(args []Interpretable) ([]ref.Val, bool) {
result := make([]ref.Val, len(args))
argloop:
for nIdx := len(args) - 1; nIdx >= 0; nIdx-- {
for idx := len(*s) - 1; idx >= 0; idx-- {
if (*s)[idx].ID == args[nIdx].ID() {
el := (*s)[idx]
*s = (*s)[:idx]
result[nIdx] = el.Val
continue argloop
}
}
return nil, false
}
return result, true
}
// Copyright 2018 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package parser
import (
"github.com/google/cel-go/common"
)
// parseErrors is a specialization of Errors.
type parseErrors struct {
errs *common.Errors
}
// errorCount indicates the number of errors reported.
func (e *parseErrors) errorCount() int {
return len(e.errs.GetErrors())
}
func (e *parseErrors) internalError(message string) {
e.errs.ReportErrorAtID(0, common.NoLocation, "%s", message)
}
func (e *parseErrors) syntaxError(l common.Location, message string) {
e.errs.ReportErrorAtID(0, l, "Syntax error: %s", message)
}
func (e *parseErrors) reportErrorAtID(id int64, l common.Location, message string, args ...any) {
e.errs.ReportErrorAtID(id, l, message, args...)
}
// Code generated from /usr/local/google/home/jdtatum/github/cel-go/parser/gen/CEL.g4 by ANTLR 4.13.1. DO NOT EDIT.
package gen // CEL
import "github.com/antlr4-go/antlr/v4"
// BaseCELListener is a complete listener for a parse tree produced by CELParser.
type BaseCELListener struct{}
var _ CELListener = &BaseCELListener{}
// VisitTerminal is called when a terminal node is visited.
func (s *BaseCELListener) VisitTerminal(node antlr.TerminalNode) {}
// VisitErrorNode is called when an error node is visited.
func (s *BaseCELListener) VisitErrorNode(node antlr.ErrorNode) {}
// EnterEveryRule is called when any rule is entered.
func (s *BaseCELListener) EnterEveryRule(ctx antlr.ParserRuleContext) {}
// ExitEveryRule is called when any rule is exited.
func (s *BaseCELListener) ExitEveryRule(ctx antlr.ParserRuleContext) {}
// EnterStart is called when production start is entered.
func (s *BaseCELListener) EnterStart(ctx *StartContext) {}
// ExitStart is called when production start is exited.
func (s *BaseCELListener) ExitStart(ctx *StartContext) {}
// EnterExpr is called when production expr is entered.
func (s *BaseCELListener) EnterExpr(ctx *ExprContext) {}
// ExitExpr is called when production expr is exited.
func (s *BaseCELListener) ExitExpr(ctx *ExprContext) {}
// EnterConditionalOr is called when production conditionalOr is entered.
func (s *BaseCELListener) EnterConditionalOr(ctx *ConditionalOrContext) {}
// ExitConditionalOr is called when production conditionalOr is exited.
func (s *BaseCELListener) ExitConditionalOr(ctx *ConditionalOrContext) {}
// EnterConditionalAnd is called when production conditionalAnd is entered.
func (s *BaseCELListener) EnterConditionalAnd(ctx *ConditionalAndContext) {}
// ExitConditionalAnd is called when production conditionalAnd is exited.
func (s *BaseCELListener) ExitConditionalAnd(ctx *ConditionalAndContext) {}
// EnterRelation is called when production relation is entered.
func (s *BaseCELListener) EnterRelation(ctx *RelationContext) {}
// ExitRelation is called when production relation is exited.
func (s *BaseCELListener) ExitRelation(ctx *RelationContext) {}
// EnterCalc is called when production calc is entered.
func (s *BaseCELListener) EnterCalc(ctx *CalcContext) {}
// ExitCalc is called when production calc is exited.
func (s *BaseCELListener) ExitCalc(ctx *CalcContext) {}
// EnterMemberExpr is called when production MemberExpr is entered.
func (s *BaseCELListener) EnterMemberExpr(ctx *MemberExprContext) {}
// ExitMemberExpr is called when production MemberExpr is exited.
func (s *BaseCELListener) ExitMemberExpr(ctx *MemberExprContext) {}
// EnterLogicalNot is called when production LogicalNot is entered.
func (s *BaseCELListener) EnterLogicalNot(ctx *LogicalNotContext) {}
// ExitLogicalNot is called when production LogicalNot is exited.
func (s *BaseCELListener) ExitLogicalNot(ctx *LogicalNotContext) {}
// EnterNegate is called when production Negate is entered.
func (s *BaseCELListener) EnterNegate(ctx *NegateContext) {}
// ExitNegate is called when production Negate is exited.
func (s *BaseCELListener) ExitNegate(ctx *NegateContext) {}
// EnterMemberCall is called when production MemberCall is entered.
func (s *BaseCELListener) EnterMemberCall(ctx *MemberCallContext) {}
// ExitMemberCall is called when production MemberCall is exited.
func (s *BaseCELListener) ExitMemberCall(ctx *MemberCallContext) {}
// EnterSelect is called when production Select is entered.
func (s *BaseCELListener) EnterSelect(ctx *SelectContext) {}
// ExitSelect is called when production Select is exited.
func (s *BaseCELListener) ExitSelect(ctx *SelectContext) {}
// EnterPrimaryExpr is called when production PrimaryExpr is entered.
func (s *BaseCELListener) EnterPrimaryExpr(ctx *PrimaryExprContext) {}
// ExitPrimaryExpr is called when production PrimaryExpr is exited.
func (s *BaseCELListener) ExitPrimaryExpr(ctx *PrimaryExprContext) {}
// EnterIndex is called when production Index is entered.
func (s *BaseCELListener) EnterIndex(ctx *IndexContext) {}
// ExitIndex is called when production Index is exited.
func (s *BaseCELListener) ExitIndex(ctx *IndexContext) {}
// EnterIdent is called when production Ident is entered.
func (s *BaseCELListener) EnterIdent(ctx *IdentContext) {}
// ExitIdent is called when production Ident is exited.
func (s *BaseCELListener) ExitIdent(ctx *IdentContext) {}
// EnterGlobalCall is called when production GlobalCall is entered.
func (s *BaseCELListener) EnterGlobalCall(ctx *GlobalCallContext) {}
// ExitGlobalCall is called when production GlobalCall is exited.
func (s *BaseCELListener) ExitGlobalCall(ctx *GlobalCallContext) {}
// EnterNested is called when production Nested is entered.
func (s *BaseCELListener) EnterNested(ctx *NestedContext) {}
// ExitNested is called when production Nested is exited.
func (s *BaseCELListener) ExitNested(ctx *NestedContext) {}
// EnterCreateList is called when production CreateList is entered.
func (s *BaseCELListener) EnterCreateList(ctx *CreateListContext) {}
// ExitCreateList is called when production CreateList is exited.
func (s *BaseCELListener) ExitCreateList(ctx *CreateListContext) {}
// EnterCreateStruct is called when production CreateStruct is entered.
func (s *BaseCELListener) EnterCreateStruct(ctx *CreateStructContext) {}
// ExitCreateStruct is called when production CreateStruct is exited.
func (s *BaseCELListener) ExitCreateStruct(ctx *CreateStructContext) {}
// EnterCreateMessage is called when production CreateMessage is entered.
func (s *BaseCELListener) EnterCreateMessage(ctx *CreateMessageContext) {}
// ExitCreateMessage is called when production CreateMessage is exited.
func (s *BaseCELListener) ExitCreateMessage(ctx *CreateMessageContext) {}
// EnterConstantLiteral is called when production ConstantLiteral is entered.
func (s *BaseCELListener) EnterConstantLiteral(ctx *ConstantLiteralContext) {}
// ExitConstantLiteral is called when production ConstantLiteral is exited.
func (s *BaseCELListener) ExitConstantLiteral(ctx *ConstantLiteralContext) {}
// EnterExprList is called when production exprList is entered.
func (s *BaseCELListener) EnterExprList(ctx *ExprListContext) {}
// ExitExprList is called when production exprList is exited.
func (s *BaseCELListener) ExitExprList(ctx *ExprListContext) {}
// EnterListInit is called when production listInit is entered.
func (s *BaseCELListener) EnterListInit(ctx *ListInitContext) {}
// ExitListInit is called when production listInit is exited.
func (s *BaseCELListener) ExitListInit(ctx *ListInitContext) {}
// EnterFieldInitializerList is called when production fieldInitializerList is entered.
func (s *BaseCELListener) EnterFieldInitializerList(ctx *FieldInitializerListContext) {}
// ExitFieldInitializerList is called when production fieldInitializerList is exited.
func (s *BaseCELListener) ExitFieldInitializerList(ctx *FieldInitializerListContext) {}
// EnterOptField is called when production optField is entered.
func (s *BaseCELListener) EnterOptField(ctx *OptFieldContext) {}
// ExitOptField is called when production optField is exited.
func (s *BaseCELListener) ExitOptField(ctx *OptFieldContext) {}
// EnterMapInitializerList is called when production mapInitializerList is entered.
func (s *BaseCELListener) EnterMapInitializerList(ctx *MapInitializerListContext) {}
// ExitMapInitializerList is called when production mapInitializerList is exited.
func (s *BaseCELListener) ExitMapInitializerList(ctx *MapInitializerListContext) {}
// EnterSimpleIdentifier is called when production SimpleIdentifier is entered.
func (s *BaseCELListener) EnterSimpleIdentifier(ctx *SimpleIdentifierContext) {}
// ExitSimpleIdentifier is called when production SimpleIdentifier is exited.
func (s *BaseCELListener) ExitSimpleIdentifier(ctx *SimpleIdentifierContext) {}
// EnterEscapedIdentifier is called when production EscapedIdentifier is entered.
func (s *BaseCELListener) EnterEscapedIdentifier(ctx *EscapedIdentifierContext) {}
// ExitEscapedIdentifier is called when production EscapedIdentifier is exited.
func (s *BaseCELListener) ExitEscapedIdentifier(ctx *EscapedIdentifierContext) {}
// EnterOptExpr is called when production optExpr is entered.
func (s *BaseCELListener) EnterOptExpr(ctx *OptExprContext) {}
// ExitOptExpr is called when production optExpr is exited.
func (s *BaseCELListener) ExitOptExpr(ctx *OptExprContext) {}
// EnterInt is called when production Int is entered.
func (s *BaseCELListener) EnterInt(ctx *IntContext) {}
// ExitInt is called when production Int is exited.
func (s *BaseCELListener) ExitInt(ctx *IntContext) {}
// EnterUint is called when production Uint is entered.
func (s *BaseCELListener) EnterUint(ctx *UintContext) {}
// ExitUint is called when production Uint is exited.
func (s *BaseCELListener) ExitUint(ctx *UintContext) {}
// EnterDouble is called when production Double is entered.
func (s *BaseCELListener) EnterDouble(ctx *DoubleContext) {}
// ExitDouble is called when production Double is exited.
func (s *BaseCELListener) ExitDouble(ctx *DoubleContext) {}
// EnterString is called when production String is entered.
func (s *BaseCELListener) EnterString(ctx *StringContext) {}
// ExitString is called when production String is exited.
func (s *BaseCELListener) ExitString(ctx *StringContext) {}
// EnterBytes is called when production Bytes is entered.
func (s *BaseCELListener) EnterBytes(ctx *BytesContext) {}
// ExitBytes is called when production Bytes is exited.
func (s *BaseCELListener) ExitBytes(ctx *BytesContext) {}
// EnterBoolTrue is called when production BoolTrue is entered.
func (s *BaseCELListener) EnterBoolTrue(ctx *BoolTrueContext) {}
// ExitBoolTrue is called when production BoolTrue is exited.
func (s *BaseCELListener) ExitBoolTrue(ctx *BoolTrueContext) {}
// EnterBoolFalse is called when production BoolFalse is entered.
func (s *BaseCELListener) EnterBoolFalse(ctx *BoolFalseContext) {}
// ExitBoolFalse is called when production BoolFalse is exited.
func (s *BaseCELListener) ExitBoolFalse(ctx *BoolFalseContext) {}
// EnterNull is called when production Null is entered.
func (s *BaseCELListener) EnterNull(ctx *NullContext) {}
// ExitNull is called when production Null is exited.
func (s *BaseCELListener) ExitNull(ctx *NullContext) {}
// Code generated from /usr/local/google/home/jdtatum/github/cel-go/parser/gen/CEL.g4 by ANTLR 4.13.1. DO NOT EDIT.
package gen // CEL
import "github.com/antlr4-go/antlr/v4"
type BaseCELVisitor struct {
*antlr.BaseParseTreeVisitor
}
func (v *BaseCELVisitor) VisitStart(ctx *StartContext) interface{} {
return v.VisitChildren(ctx)
}
func (v *BaseCELVisitor) VisitExpr(ctx *ExprContext) interface{} {
return v.VisitChildren(ctx)
}
func (v *BaseCELVisitor) VisitConditionalOr(ctx *ConditionalOrContext) interface{} {
return v.VisitChildren(ctx)
}
func (v *BaseCELVisitor) VisitConditionalAnd(ctx *ConditionalAndContext) interface{} {
return v.VisitChildren(ctx)
}
func (v *BaseCELVisitor) VisitRelation(ctx *RelationContext) interface{} {
return v.VisitChildren(ctx)
}
func (v *BaseCELVisitor) VisitCalc(ctx *CalcContext) interface{} {
return v.VisitChildren(ctx)
}
func (v *BaseCELVisitor) VisitMemberExpr(ctx *MemberExprContext) interface{} {
return v.VisitChildren(ctx)
}
func (v *BaseCELVisitor) VisitLogicalNot(ctx *LogicalNotContext) interface{} {
return v.VisitChildren(ctx)
}
func (v *BaseCELVisitor) VisitNegate(ctx *NegateContext) interface{} {
return v.VisitChildren(ctx)
}
func (v *BaseCELVisitor) VisitMemberCall(ctx *MemberCallContext) interface{} {
return v.VisitChildren(ctx)
}
func (v *BaseCELVisitor) VisitSelect(ctx *SelectContext) interface{} {
return v.VisitChildren(ctx)
}
func (v *BaseCELVisitor) VisitPrimaryExpr(ctx *PrimaryExprContext) interface{} {
return v.VisitChildren(ctx)
}
func (v *BaseCELVisitor) VisitIndex(ctx *IndexContext) interface{} {
return v.VisitChildren(ctx)
}
func (v *BaseCELVisitor) VisitIdent(ctx *IdentContext) interface{} {
return v.VisitChildren(ctx)
}
func (v *BaseCELVisitor) VisitGlobalCall(ctx *GlobalCallContext) interface{} {
return v.VisitChildren(ctx)
}
func (v *BaseCELVisitor) VisitNested(ctx *NestedContext) interface{} {
return v.VisitChildren(ctx)
}
func (v *BaseCELVisitor) VisitCreateList(ctx *CreateListContext) interface{} {
return v.VisitChildren(ctx)
}
func (v *BaseCELVisitor) VisitCreateStruct(ctx *CreateStructContext) interface{} {
return v.VisitChildren(ctx)
}
func (v *BaseCELVisitor) VisitCreateMessage(ctx *CreateMessageContext) interface{} {
return v.VisitChildren(ctx)
}
func (v *BaseCELVisitor) VisitConstantLiteral(ctx *ConstantLiteralContext) interface{} {
return v.VisitChildren(ctx)
}
func (v *BaseCELVisitor) VisitExprList(ctx *ExprListContext) interface{} {
return v.VisitChildren(ctx)
}
func (v *BaseCELVisitor) VisitListInit(ctx *ListInitContext) interface{} {
return v.VisitChildren(ctx)
}
func (v *BaseCELVisitor) VisitFieldInitializerList(ctx *FieldInitializerListContext) interface{} {
return v.VisitChildren(ctx)
}
func (v *BaseCELVisitor) VisitOptField(ctx *OptFieldContext) interface{} {
return v.VisitChildren(ctx)
}
func (v *BaseCELVisitor) VisitMapInitializerList(ctx *MapInitializerListContext) interface{} {
return v.VisitChildren(ctx)
}
func (v *BaseCELVisitor) VisitSimpleIdentifier(ctx *SimpleIdentifierContext) interface{} {
return v.VisitChildren(ctx)
}
func (v *BaseCELVisitor) VisitEscapedIdentifier(ctx *EscapedIdentifierContext) interface{} {
return v.VisitChildren(ctx)
}
func (v *BaseCELVisitor) VisitOptExpr(ctx *OptExprContext) interface{} {
return v.VisitChildren(ctx)
}
func (v *BaseCELVisitor) VisitInt(ctx *IntContext) interface{} {
return v.VisitChildren(ctx)
}
func (v *BaseCELVisitor) VisitUint(ctx *UintContext) interface{} {
return v.VisitChildren(ctx)
}
func (v *BaseCELVisitor) VisitDouble(ctx *DoubleContext) interface{} {
return v.VisitChildren(ctx)
}
func (v *BaseCELVisitor) VisitString(ctx *StringContext) interface{} {
return v.VisitChildren(ctx)
}
func (v *BaseCELVisitor) VisitBytes(ctx *BytesContext) interface{} {
return v.VisitChildren(ctx)
}
func (v *BaseCELVisitor) VisitBoolTrue(ctx *BoolTrueContext) interface{} {
return v.VisitChildren(ctx)
}
func (v *BaseCELVisitor) VisitBoolFalse(ctx *BoolFalseContext) interface{} {
return v.VisitChildren(ctx)
}
func (v *BaseCELVisitor) VisitNull(ctx *NullContext) interface{} {
return v.VisitChildren(ctx)
}
// Code generated from /usr/local/google/home/jdtatum/github/cel-go/parser/gen/CEL.g4 by ANTLR 4.13.1. DO NOT EDIT.
package gen
import (
"fmt"
"github.com/antlr4-go/antlr/v4"
"sync"
"unicode"
)
// Suppress unused import error
var _ = fmt.Printf
var _ = sync.Once{}
var _ = unicode.IsLetter
type CELLexer struct {
*antlr.BaseLexer
channelNames []string
modeNames []string
// TODO: EOF string
}
var CELLexerLexerStaticData struct {
once sync.Once
serializedATN []int32
ChannelNames []string
ModeNames []string
LiteralNames []string
SymbolicNames []string
RuleNames []string
PredictionContextCache *antlr.PredictionContextCache
atn *antlr.ATN
decisionToDFA []*antlr.DFA
}
func cellexerLexerInit() {
staticData := &CELLexerLexerStaticData
staticData.ChannelNames = []string{
"DEFAULT_TOKEN_CHANNEL", "HIDDEN",
}
staticData.ModeNames = []string{
"DEFAULT_MODE",
}
staticData.LiteralNames = []string{
"", "'=='", "'!='", "'in'", "'<'", "'<='", "'>='", "'>'", "'&&'", "'||'",
"'['", "']'", "'{'", "'}'", "'('", "')'", "'.'", "','", "'-'", "'!'",
"'?'", "':'", "'+'", "'*'", "'/'", "'%'", "'true'", "'false'", "'null'",
}
staticData.SymbolicNames = []string{
"", "EQUALS", "NOT_EQUALS", "IN", "LESS", "LESS_EQUALS", "GREATER_EQUALS",
"GREATER", "LOGICAL_AND", "LOGICAL_OR", "LBRACKET", "RPRACKET", "LBRACE",
"RBRACE", "LPAREN", "RPAREN", "DOT", "COMMA", "MINUS", "EXCLAM", "QUESTIONMARK",
"COLON", "PLUS", "STAR", "SLASH", "PERCENT", "CEL_TRUE", "CEL_FALSE",
"NUL", "WHITESPACE", "COMMENT", "NUM_FLOAT", "NUM_INT", "NUM_UINT",
"STRING", "BYTES", "IDENTIFIER", "ESC_IDENTIFIER",
}
staticData.RuleNames = []string{
"EQUALS", "NOT_EQUALS", "IN", "LESS", "LESS_EQUALS", "GREATER_EQUALS",
"GREATER", "LOGICAL_AND", "LOGICAL_OR", "LBRACKET", "RPRACKET", "LBRACE",
"RBRACE", "LPAREN", "RPAREN", "DOT", "COMMA", "MINUS", "EXCLAM", "QUESTIONMARK",
"COLON", "PLUS", "STAR", "SLASH", "PERCENT", "CEL_TRUE", "CEL_FALSE",
"NUL", "BACKSLASH", "LETTER", "DIGIT", "EXPONENT", "HEXDIGIT", "RAW",
"ESC_SEQ", "ESC_CHAR_SEQ", "ESC_OCT_SEQ", "ESC_BYTE_SEQ", "ESC_UNI_SEQ",
"WHITESPACE", "COMMENT", "NUM_FLOAT", "NUM_INT", "NUM_UINT", "STRING",
"BYTES", "IDENTIFIER", "ESC_IDENTIFIER",
}
staticData.PredictionContextCache = antlr.NewPredictionContextCache()
staticData.serializedATN = []int32{
4, 0, 37, 435, 6, -1, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2,
4, 7, 4, 2, 5, 7, 5, 2, 6, 7, 6, 2, 7, 7, 7, 2, 8, 7, 8, 2, 9, 7, 9, 2,
10, 7, 10, 2, 11, 7, 11, 2, 12, 7, 12, 2, 13, 7, 13, 2, 14, 7, 14, 2, 15,
7, 15, 2, 16, 7, 16, 2, 17, 7, 17, 2, 18, 7, 18, 2, 19, 7, 19, 2, 20, 7,
20, 2, 21, 7, 21, 2, 22, 7, 22, 2, 23, 7, 23, 2, 24, 7, 24, 2, 25, 7, 25,
2, 26, 7, 26, 2, 27, 7, 27, 2, 28, 7, 28, 2, 29, 7, 29, 2, 30, 7, 30, 2,
31, 7, 31, 2, 32, 7, 32, 2, 33, 7, 33, 2, 34, 7, 34, 2, 35, 7, 35, 2, 36,
7, 36, 2, 37, 7, 37, 2, 38, 7, 38, 2, 39, 7, 39, 2, 40, 7, 40, 2, 41, 7,
41, 2, 42, 7, 42, 2, 43, 7, 43, 2, 44, 7, 44, 2, 45, 7, 45, 2, 46, 7, 46,
2, 47, 7, 47, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 2, 1, 2, 1, 2, 1,
3, 1, 3, 1, 4, 1, 4, 1, 4, 1, 5, 1, 5, 1, 5, 1, 6, 1, 6, 1, 7, 1, 7, 1,
7, 1, 8, 1, 8, 1, 8, 1, 9, 1, 9, 1, 10, 1, 10, 1, 11, 1, 11, 1, 12, 1,
12, 1, 13, 1, 13, 1, 14, 1, 14, 1, 15, 1, 15, 1, 16, 1, 16, 1, 17, 1, 17,
1, 18, 1, 18, 1, 19, 1, 19, 1, 20, 1, 20, 1, 21, 1, 21, 1, 22, 1, 22, 1,
23, 1, 23, 1, 24, 1, 24, 1, 25, 1, 25, 1, 25, 1, 25, 1, 25, 1, 26, 1, 26,
1, 26, 1, 26, 1, 26, 1, 26, 1, 27, 1, 27, 1, 27, 1, 27, 1, 27, 1, 28, 1,
28, 1, 29, 1, 29, 1, 30, 1, 30, 1, 31, 1, 31, 3, 31, 179, 8, 31, 1, 31,
4, 31, 182, 8, 31, 11, 31, 12, 31, 183, 1, 32, 1, 32, 1, 33, 1, 33, 1,
34, 1, 34, 1, 34, 1, 34, 3, 34, 194, 8, 34, 1, 35, 1, 35, 1, 35, 1, 36,
1, 36, 1, 36, 1, 36, 1, 36, 1, 37, 1, 37, 1, 37, 1, 37, 1, 37, 1, 38, 1,
38, 1, 38, 1, 38, 1, 38, 1, 38, 1, 38, 1, 38, 1, 38, 1, 38, 1, 38, 1, 38,
1, 38, 1, 38, 1, 38, 1, 38, 1, 38, 1, 38, 3, 38, 227, 8, 38, 1, 39, 4,
39, 230, 8, 39, 11, 39, 12, 39, 231, 1, 39, 1, 39, 1, 40, 1, 40, 1, 40,
1, 40, 5, 40, 240, 8, 40, 10, 40, 12, 40, 243, 9, 40, 1, 40, 1, 40, 1,
41, 4, 41, 248, 8, 41, 11, 41, 12, 41, 249, 1, 41, 1, 41, 4, 41, 254, 8,
41, 11, 41, 12, 41, 255, 1, 41, 3, 41, 259, 8, 41, 1, 41, 4, 41, 262, 8,
41, 11, 41, 12, 41, 263, 1, 41, 1, 41, 1, 41, 1, 41, 4, 41, 270, 8, 41,
11, 41, 12, 41, 271, 1, 41, 3, 41, 275, 8, 41, 3, 41, 277, 8, 41, 1, 42,
4, 42, 280, 8, 42, 11, 42, 12, 42, 281, 1, 42, 1, 42, 1, 42, 1, 42, 4,
42, 288, 8, 42, 11, 42, 12, 42, 289, 3, 42, 292, 8, 42, 1, 43, 4, 43, 295,
8, 43, 11, 43, 12, 43, 296, 1, 43, 1, 43, 1, 43, 1, 43, 1, 43, 1, 43, 4,
43, 305, 8, 43, 11, 43, 12, 43, 306, 1, 43, 1, 43, 3, 43, 311, 8, 43, 1,
44, 1, 44, 1, 44, 5, 44, 316, 8, 44, 10, 44, 12, 44, 319, 9, 44, 1, 44,
1, 44, 1, 44, 1, 44, 5, 44, 325, 8, 44, 10, 44, 12, 44, 328, 9, 44, 1,
44, 1, 44, 1, 44, 1, 44, 1, 44, 1, 44, 1, 44, 5, 44, 337, 8, 44, 10, 44,
12, 44, 340, 9, 44, 1, 44, 1, 44, 1, 44, 1, 44, 1, 44, 1, 44, 1, 44, 1,
44, 1, 44, 5, 44, 351, 8, 44, 10, 44, 12, 44, 354, 9, 44, 1, 44, 1, 44,
1, 44, 1, 44, 1, 44, 1, 44, 5, 44, 362, 8, 44, 10, 44, 12, 44, 365, 9,
44, 1, 44, 1, 44, 1, 44, 1, 44, 1, 44, 5, 44, 372, 8, 44, 10, 44, 12, 44,
375, 9, 44, 1, 44, 1, 44, 1, 44, 1, 44, 1, 44, 1, 44, 1, 44, 1, 44, 5,
44, 385, 8, 44, 10, 44, 12, 44, 388, 9, 44, 1, 44, 1, 44, 1, 44, 1, 44,
1, 44, 1, 44, 1, 44, 1, 44, 1, 44, 1, 44, 5, 44, 400, 8, 44, 10, 44, 12,
44, 403, 9, 44, 1, 44, 1, 44, 1, 44, 1, 44, 3, 44, 409, 8, 44, 1, 45, 1,
45, 1, 45, 1, 46, 1, 46, 3, 46, 416, 8, 46, 1, 46, 1, 46, 1, 46, 5, 46,
421, 8, 46, 10, 46, 12, 46, 424, 9, 46, 1, 47, 1, 47, 1, 47, 1, 47, 4,
47, 430, 8, 47, 11, 47, 12, 47, 431, 1, 47, 1, 47, 4, 338, 352, 386, 401,
0, 48, 1, 1, 3, 2, 5, 3, 7, 4, 9, 5, 11, 6, 13, 7, 15, 8, 17, 9, 19, 10,
21, 11, 23, 12, 25, 13, 27, 14, 29, 15, 31, 16, 33, 17, 35, 18, 37, 19,
39, 20, 41, 21, 43, 22, 45, 23, 47, 24, 49, 25, 51, 26, 53, 27, 55, 28,
57, 0, 59, 0, 61, 0, 63, 0, 65, 0, 67, 0, 69, 0, 71, 0, 73, 0, 75, 0, 77,
0, 79, 29, 81, 30, 83, 31, 85, 32, 87, 33, 89, 34, 91, 35, 93, 36, 95,
37, 1, 0, 17, 2, 0, 65, 90, 97, 122, 2, 0, 69, 69, 101, 101, 2, 0, 43,
43, 45, 45, 3, 0, 48, 57, 65, 70, 97, 102, 2, 0, 82, 82, 114, 114, 10,
0, 34, 34, 39, 39, 63, 63, 92, 92, 96, 98, 102, 102, 110, 110, 114, 114,
116, 116, 118, 118, 2, 0, 88, 88, 120, 120, 3, 0, 9, 10, 12, 13, 32, 32,
1, 0, 10, 10, 2, 0, 85, 85, 117, 117, 4, 0, 10, 10, 13, 13, 34, 34, 92,
92, 4, 0, 10, 10, 13, 13, 39, 39, 92, 92, 1, 0, 92, 92, 3, 0, 10, 10, 13,
13, 34, 34, 3, 0, 10, 10, 13, 13, 39, 39, 2, 0, 66, 66, 98, 98, 3, 0, 32,
32, 45, 47, 95, 95, 471, 0, 1, 1, 0, 0, 0, 0, 3, 1, 0, 0, 0, 0, 5, 1, 0,
0, 0, 0, 7, 1, 0, 0, 0, 0, 9, 1, 0, 0, 0, 0, 11, 1, 0, 0, 0, 0, 13, 1,
0, 0, 0, 0, 15, 1, 0, 0, 0, 0, 17, 1, 0, 0, 0, 0, 19, 1, 0, 0, 0, 0, 21,
1, 0, 0, 0, 0, 23, 1, 0, 0, 0, 0, 25, 1, 0, 0, 0, 0, 27, 1, 0, 0, 0, 0,
29, 1, 0, 0, 0, 0, 31, 1, 0, 0, 0, 0, 33, 1, 0, 0, 0, 0, 35, 1, 0, 0, 0,
0, 37, 1, 0, 0, 0, 0, 39, 1, 0, 0, 0, 0, 41, 1, 0, 0, 0, 0, 43, 1, 0, 0,
0, 0, 45, 1, 0, 0, 0, 0, 47, 1, 0, 0, 0, 0, 49, 1, 0, 0, 0, 0, 51, 1, 0,
0, 0, 0, 53, 1, 0, 0, 0, 0, 55, 1, 0, 0, 0, 0, 79, 1, 0, 0, 0, 0, 81, 1,
0, 0, 0, 0, 83, 1, 0, 0, 0, 0, 85, 1, 0, 0, 0, 0, 87, 1, 0, 0, 0, 0, 89,
1, 0, 0, 0, 0, 91, 1, 0, 0, 0, 0, 93, 1, 0, 0, 0, 0, 95, 1, 0, 0, 0, 1,
97, 1, 0, 0, 0, 3, 100, 1, 0, 0, 0, 5, 103, 1, 0, 0, 0, 7, 106, 1, 0, 0,
0, 9, 108, 1, 0, 0, 0, 11, 111, 1, 0, 0, 0, 13, 114, 1, 0, 0, 0, 15, 116,
1, 0, 0, 0, 17, 119, 1, 0, 0, 0, 19, 122, 1, 0, 0, 0, 21, 124, 1, 0, 0,
0, 23, 126, 1, 0, 0, 0, 25, 128, 1, 0, 0, 0, 27, 130, 1, 0, 0, 0, 29, 132,
1, 0, 0, 0, 31, 134, 1, 0, 0, 0, 33, 136, 1, 0, 0, 0, 35, 138, 1, 0, 0,
0, 37, 140, 1, 0, 0, 0, 39, 142, 1, 0, 0, 0, 41, 144, 1, 0, 0, 0, 43, 146,
1, 0, 0, 0, 45, 148, 1, 0, 0, 0, 47, 150, 1, 0, 0, 0, 49, 152, 1, 0, 0,
0, 51, 154, 1, 0, 0, 0, 53, 159, 1, 0, 0, 0, 55, 165, 1, 0, 0, 0, 57, 170,
1, 0, 0, 0, 59, 172, 1, 0, 0, 0, 61, 174, 1, 0, 0, 0, 63, 176, 1, 0, 0,
0, 65, 185, 1, 0, 0, 0, 67, 187, 1, 0, 0, 0, 69, 193, 1, 0, 0, 0, 71, 195,
1, 0, 0, 0, 73, 198, 1, 0, 0, 0, 75, 203, 1, 0, 0, 0, 77, 226, 1, 0, 0,
0, 79, 229, 1, 0, 0, 0, 81, 235, 1, 0, 0, 0, 83, 276, 1, 0, 0, 0, 85, 291,
1, 0, 0, 0, 87, 310, 1, 0, 0, 0, 89, 408, 1, 0, 0, 0, 91, 410, 1, 0, 0,
0, 93, 415, 1, 0, 0, 0, 95, 425, 1, 0, 0, 0, 97, 98, 5, 61, 0, 0, 98, 99,
5, 61, 0, 0, 99, 2, 1, 0, 0, 0, 100, 101, 5, 33, 0, 0, 101, 102, 5, 61,
0, 0, 102, 4, 1, 0, 0, 0, 103, 104, 5, 105, 0, 0, 104, 105, 5, 110, 0,
0, 105, 6, 1, 0, 0, 0, 106, 107, 5, 60, 0, 0, 107, 8, 1, 0, 0, 0, 108,
109, 5, 60, 0, 0, 109, 110, 5, 61, 0, 0, 110, 10, 1, 0, 0, 0, 111, 112,
5, 62, 0, 0, 112, 113, 5, 61, 0, 0, 113, 12, 1, 0, 0, 0, 114, 115, 5, 62,
0, 0, 115, 14, 1, 0, 0, 0, 116, 117, 5, 38, 0, 0, 117, 118, 5, 38, 0, 0,
118, 16, 1, 0, 0, 0, 119, 120, 5, 124, 0, 0, 120, 121, 5, 124, 0, 0, 121,
18, 1, 0, 0, 0, 122, 123, 5, 91, 0, 0, 123, 20, 1, 0, 0, 0, 124, 125, 5,
93, 0, 0, 125, 22, 1, 0, 0, 0, 126, 127, 5, 123, 0, 0, 127, 24, 1, 0, 0,
0, 128, 129, 5, 125, 0, 0, 129, 26, 1, 0, 0, 0, 130, 131, 5, 40, 0, 0,
131, 28, 1, 0, 0, 0, 132, 133, 5, 41, 0, 0, 133, 30, 1, 0, 0, 0, 134, 135,
5, 46, 0, 0, 135, 32, 1, 0, 0, 0, 136, 137, 5, 44, 0, 0, 137, 34, 1, 0,
0, 0, 138, 139, 5, 45, 0, 0, 139, 36, 1, 0, 0, 0, 140, 141, 5, 33, 0, 0,
141, 38, 1, 0, 0, 0, 142, 143, 5, 63, 0, 0, 143, 40, 1, 0, 0, 0, 144, 145,
5, 58, 0, 0, 145, 42, 1, 0, 0, 0, 146, 147, 5, 43, 0, 0, 147, 44, 1, 0,
0, 0, 148, 149, 5, 42, 0, 0, 149, 46, 1, 0, 0, 0, 150, 151, 5, 47, 0, 0,
151, 48, 1, 0, 0, 0, 152, 153, 5, 37, 0, 0, 153, 50, 1, 0, 0, 0, 154, 155,
5, 116, 0, 0, 155, 156, 5, 114, 0, 0, 156, 157, 5, 117, 0, 0, 157, 158,
5, 101, 0, 0, 158, 52, 1, 0, 0, 0, 159, 160, 5, 102, 0, 0, 160, 161, 5,
97, 0, 0, 161, 162, 5, 108, 0, 0, 162, 163, 5, 115, 0, 0, 163, 164, 5,
101, 0, 0, 164, 54, 1, 0, 0, 0, 165, 166, 5, 110, 0, 0, 166, 167, 5, 117,
0, 0, 167, 168, 5, 108, 0, 0, 168, 169, 5, 108, 0, 0, 169, 56, 1, 0, 0,
0, 170, 171, 5, 92, 0, 0, 171, 58, 1, 0, 0, 0, 172, 173, 7, 0, 0, 0, 173,
60, 1, 0, 0, 0, 174, 175, 2, 48, 57, 0, 175, 62, 1, 0, 0, 0, 176, 178,
7, 1, 0, 0, 177, 179, 7, 2, 0, 0, 178, 177, 1, 0, 0, 0, 178, 179, 1, 0,
0, 0, 179, 181, 1, 0, 0, 0, 180, 182, 3, 61, 30, 0, 181, 180, 1, 0, 0,
0, 182, 183, 1, 0, 0, 0, 183, 181, 1, 0, 0, 0, 183, 184, 1, 0, 0, 0, 184,
64, 1, 0, 0, 0, 185, 186, 7, 3, 0, 0, 186, 66, 1, 0, 0, 0, 187, 188, 7,
4, 0, 0, 188, 68, 1, 0, 0, 0, 189, 194, 3, 71, 35, 0, 190, 194, 3, 75,
37, 0, 191, 194, 3, 77, 38, 0, 192, 194, 3, 73, 36, 0, 193, 189, 1, 0,
0, 0, 193, 190, 1, 0, 0, 0, 193, 191, 1, 0, 0, 0, 193, 192, 1, 0, 0, 0,
194, 70, 1, 0, 0, 0, 195, 196, 3, 57, 28, 0, 196, 197, 7, 5, 0, 0, 197,
72, 1, 0, 0, 0, 198, 199, 3, 57, 28, 0, 199, 200, 2, 48, 51, 0, 200, 201,
2, 48, 55, 0, 201, 202, 2, 48, 55, 0, 202, 74, 1, 0, 0, 0, 203, 204, 3,
57, 28, 0, 204, 205, 7, 6, 0, 0, 205, 206, 3, 65, 32, 0, 206, 207, 3, 65,
32, 0, 207, 76, 1, 0, 0, 0, 208, 209, 3, 57, 28, 0, 209, 210, 5, 117, 0,
0, 210, 211, 3, 65, 32, 0, 211, 212, 3, 65, 32, 0, 212, 213, 3, 65, 32,
0, 213, 214, 3, 65, 32, 0, 214, 227, 1, 0, 0, 0, 215, 216, 3, 57, 28, 0,
216, 217, 5, 85, 0, 0, 217, 218, 3, 65, 32, 0, 218, 219, 3, 65, 32, 0,
219, 220, 3, 65, 32, 0, 220, 221, 3, 65, 32, 0, 221, 222, 3, 65, 32, 0,
222, 223, 3, 65, 32, 0, 223, 224, 3, 65, 32, 0, 224, 225, 3, 65, 32, 0,
225, 227, 1, 0, 0, 0, 226, 208, 1, 0, 0, 0, 226, 215, 1, 0, 0, 0, 227,
78, 1, 0, 0, 0, 228, 230, 7, 7, 0, 0, 229, 228, 1, 0, 0, 0, 230, 231, 1,
0, 0, 0, 231, 229, 1, 0, 0, 0, 231, 232, 1, 0, 0, 0, 232, 233, 1, 0, 0,
0, 233, 234, 6, 39, 0, 0, 234, 80, 1, 0, 0, 0, 235, 236, 5, 47, 0, 0, 236,
237, 5, 47, 0, 0, 237, 241, 1, 0, 0, 0, 238, 240, 8, 8, 0, 0, 239, 238,
1, 0, 0, 0, 240, 243, 1, 0, 0, 0, 241, 239, 1, 0, 0, 0, 241, 242, 1, 0,
0, 0, 242, 244, 1, 0, 0, 0, 243, 241, 1, 0, 0, 0, 244, 245, 6, 40, 0, 0,
245, 82, 1, 0, 0, 0, 246, 248, 3, 61, 30, 0, 247, 246, 1, 0, 0, 0, 248,
249, 1, 0, 0, 0, 249, 247, 1, 0, 0, 0, 249, 250, 1, 0, 0, 0, 250, 251,
1, 0, 0, 0, 251, 253, 5, 46, 0, 0, 252, 254, 3, 61, 30, 0, 253, 252, 1,
0, 0, 0, 254, 255, 1, 0, 0, 0, 255, 253, 1, 0, 0, 0, 255, 256, 1, 0, 0,
0, 256, 258, 1, 0, 0, 0, 257, 259, 3, 63, 31, 0, 258, 257, 1, 0, 0, 0,
258, 259, 1, 0, 0, 0, 259, 277, 1, 0, 0, 0, 260, 262, 3, 61, 30, 0, 261,
260, 1, 0, 0, 0, 262, 263, 1, 0, 0, 0, 263, 261, 1, 0, 0, 0, 263, 264,
1, 0, 0, 0, 264, 265, 1, 0, 0, 0, 265, 266, 3, 63, 31, 0, 266, 277, 1,
0, 0, 0, 267, 269, 5, 46, 0, 0, 268, 270, 3, 61, 30, 0, 269, 268, 1, 0,
0, 0, 270, 271, 1, 0, 0, 0, 271, 269, 1, 0, 0, 0, 271, 272, 1, 0, 0, 0,
272, 274, 1, 0, 0, 0, 273, 275, 3, 63, 31, 0, 274, 273, 1, 0, 0, 0, 274,
275, 1, 0, 0, 0, 275, 277, 1, 0, 0, 0, 276, 247, 1, 0, 0, 0, 276, 261,
1, 0, 0, 0, 276, 267, 1, 0, 0, 0, 277, 84, 1, 0, 0, 0, 278, 280, 3, 61,
30, 0, 279, 278, 1, 0, 0, 0, 280, 281, 1, 0, 0, 0, 281, 279, 1, 0, 0, 0,
281, 282, 1, 0, 0, 0, 282, 292, 1, 0, 0, 0, 283, 284, 5, 48, 0, 0, 284,
285, 5, 120, 0, 0, 285, 287, 1, 0, 0, 0, 286, 288, 3, 65, 32, 0, 287, 286,
1, 0, 0, 0, 288, 289, 1, 0, 0, 0, 289, 287, 1, 0, 0, 0, 289, 290, 1, 0,
0, 0, 290, 292, 1, 0, 0, 0, 291, 279, 1, 0, 0, 0, 291, 283, 1, 0, 0, 0,
292, 86, 1, 0, 0, 0, 293, 295, 3, 61, 30, 0, 294, 293, 1, 0, 0, 0, 295,
296, 1, 0, 0, 0, 296, 294, 1, 0, 0, 0, 296, 297, 1, 0, 0, 0, 297, 298,
1, 0, 0, 0, 298, 299, 7, 9, 0, 0, 299, 311, 1, 0, 0, 0, 300, 301, 5, 48,
0, 0, 301, 302, 5, 120, 0, 0, 302, 304, 1, 0, 0, 0, 303, 305, 3, 65, 32,
0, 304, 303, 1, 0, 0, 0, 305, 306, 1, 0, 0, 0, 306, 304, 1, 0, 0, 0, 306,
307, 1, 0, 0, 0, 307, 308, 1, 0, 0, 0, 308, 309, 7, 9, 0, 0, 309, 311,
1, 0, 0, 0, 310, 294, 1, 0, 0, 0, 310, 300, 1, 0, 0, 0, 311, 88, 1, 0,
0, 0, 312, 317, 5, 34, 0, 0, 313, 316, 3, 69, 34, 0, 314, 316, 8, 10, 0,
0, 315, 313, 1, 0, 0, 0, 315, 314, 1, 0, 0, 0, 316, 319, 1, 0, 0, 0, 317,
315, 1, 0, 0, 0, 317, 318, 1, 0, 0, 0, 318, 320, 1, 0, 0, 0, 319, 317,
1, 0, 0, 0, 320, 409, 5, 34, 0, 0, 321, 326, 5, 39, 0, 0, 322, 325, 3,
69, 34, 0, 323, 325, 8, 11, 0, 0, 324, 322, 1, 0, 0, 0, 324, 323, 1, 0,
0, 0, 325, 328, 1, 0, 0, 0, 326, 324, 1, 0, 0, 0, 326, 327, 1, 0, 0, 0,
327, 329, 1, 0, 0, 0, 328, 326, 1, 0, 0, 0, 329, 409, 5, 39, 0, 0, 330,
331, 5, 34, 0, 0, 331, 332, 5, 34, 0, 0, 332, 333, 5, 34, 0, 0, 333, 338,
1, 0, 0, 0, 334, 337, 3, 69, 34, 0, 335, 337, 8, 12, 0, 0, 336, 334, 1,
0, 0, 0, 336, 335, 1, 0, 0, 0, 337, 340, 1, 0, 0, 0, 338, 339, 1, 0, 0,
0, 338, 336, 1, 0, 0, 0, 339, 341, 1, 0, 0, 0, 340, 338, 1, 0, 0, 0, 341,
342, 5, 34, 0, 0, 342, 343, 5, 34, 0, 0, 343, 409, 5, 34, 0, 0, 344, 345,
5, 39, 0, 0, 345, 346, 5, 39, 0, 0, 346, 347, 5, 39, 0, 0, 347, 352, 1,
0, 0, 0, 348, 351, 3, 69, 34, 0, 349, 351, 8, 12, 0, 0, 350, 348, 1, 0,
0, 0, 350, 349, 1, 0, 0, 0, 351, 354, 1, 0, 0, 0, 352, 353, 1, 0, 0, 0,
352, 350, 1, 0, 0, 0, 353, 355, 1, 0, 0, 0, 354, 352, 1, 0, 0, 0, 355,
356, 5, 39, 0, 0, 356, 357, 5, 39, 0, 0, 357, 409, 5, 39, 0, 0, 358, 359,
3, 67, 33, 0, 359, 363, 5, 34, 0, 0, 360, 362, 8, 13, 0, 0, 361, 360, 1,
0, 0, 0, 362, 365, 1, 0, 0, 0, 363, 361, 1, 0, 0, 0, 363, 364, 1, 0, 0,
0, 364, 366, 1, 0, 0, 0, 365, 363, 1, 0, 0, 0, 366, 367, 5, 34, 0, 0, 367,
409, 1, 0, 0, 0, 368, 369, 3, 67, 33, 0, 369, 373, 5, 39, 0, 0, 370, 372,
8, 14, 0, 0, 371, 370, 1, 0, 0, 0, 372, 375, 1, 0, 0, 0, 373, 371, 1, 0,
0, 0, 373, 374, 1, 0, 0, 0, 374, 376, 1, 0, 0, 0, 375, 373, 1, 0, 0, 0,
376, 377, 5, 39, 0, 0, 377, 409, 1, 0, 0, 0, 378, 379, 3, 67, 33, 0, 379,
380, 5, 34, 0, 0, 380, 381, 5, 34, 0, 0, 381, 382, 5, 34, 0, 0, 382, 386,
1, 0, 0, 0, 383, 385, 9, 0, 0, 0, 384, 383, 1, 0, 0, 0, 385, 388, 1, 0,
0, 0, 386, 387, 1, 0, 0, 0, 386, 384, 1, 0, 0, 0, 387, 389, 1, 0, 0, 0,
388, 386, 1, 0, 0, 0, 389, 390, 5, 34, 0, 0, 390, 391, 5, 34, 0, 0, 391,
392, 5, 34, 0, 0, 392, 409, 1, 0, 0, 0, 393, 394, 3, 67, 33, 0, 394, 395,
5, 39, 0, 0, 395, 396, 5, 39, 0, 0, 396, 397, 5, 39, 0, 0, 397, 401, 1,
0, 0, 0, 398, 400, 9, 0, 0, 0, 399, 398, 1, 0, 0, 0, 400, 403, 1, 0, 0,
0, 401, 402, 1, 0, 0, 0, 401, 399, 1, 0, 0, 0, 402, 404, 1, 0, 0, 0, 403,
401, 1, 0, 0, 0, 404, 405, 5, 39, 0, 0, 405, 406, 5, 39, 0, 0, 406, 407,
5, 39, 0, 0, 407, 409, 1, 0, 0, 0, 408, 312, 1, 0, 0, 0, 408, 321, 1, 0,
0, 0, 408, 330, 1, 0, 0, 0, 408, 344, 1, 0, 0, 0, 408, 358, 1, 0, 0, 0,
408, 368, 1, 0, 0, 0, 408, 378, 1, 0, 0, 0, 408, 393, 1, 0, 0, 0, 409,
90, 1, 0, 0, 0, 410, 411, 7, 15, 0, 0, 411, 412, 3, 89, 44, 0, 412, 92,
1, 0, 0, 0, 413, 416, 3, 59, 29, 0, 414, 416, 5, 95, 0, 0, 415, 413, 1,
0, 0, 0, 415, 414, 1, 0, 0, 0, 416, 422, 1, 0, 0, 0, 417, 421, 3, 59, 29,
0, 418, 421, 3, 61, 30, 0, 419, 421, 5, 95, 0, 0, 420, 417, 1, 0, 0, 0,
420, 418, 1, 0, 0, 0, 420, 419, 1, 0, 0, 0, 421, 424, 1, 0, 0, 0, 422,
420, 1, 0, 0, 0, 422, 423, 1, 0, 0, 0, 423, 94, 1, 0, 0, 0, 424, 422, 1,
0, 0, 0, 425, 429, 5, 96, 0, 0, 426, 430, 3, 59, 29, 0, 427, 430, 3, 61,
30, 0, 428, 430, 7, 16, 0, 0, 429, 426, 1, 0, 0, 0, 429, 427, 1, 0, 0,
0, 429, 428, 1, 0, 0, 0, 430, 431, 1, 0, 0, 0, 431, 429, 1, 0, 0, 0, 431,
432, 1, 0, 0, 0, 432, 433, 1, 0, 0, 0, 433, 434, 5, 96, 0, 0, 434, 96,
1, 0, 0, 0, 38, 0, 178, 183, 193, 226, 231, 241, 249, 255, 258, 263, 271,
274, 276, 281, 289, 291, 296, 306, 310, 315, 317, 324, 326, 336, 338, 350,
352, 363, 373, 386, 401, 408, 415, 420, 422, 429, 431, 1, 0, 1, 0,
}
deserializer := antlr.NewATNDeserializer(nil)
staticData.atn = deserializer.Deserialize(staticData.serializedATN)
atn := staticData.atn
staticData.decisionToDFA = make([]*antlr.DFA, len(atn.DecisionToState))
decisionToDFA := staticData.decisionToDFA
for index, state := range atn.DecisionToState {
decisionToDFA[index] = antlr.NewDFA(state, index)
}
}
// CELLexerInit initializes any static state used to implement CELLexer. By default the
// static state used to implement the lexer is lazily initialized during the first call to
// NewCELLexer(). You can call this function if you wish to initialize the static state ahead
// of time.
func CELLexerInit() {
staticData := &CELLexerLexerStaticData
staticData.once.Do(cellexerLexerInit)
}
// NewCELLexer produces a new lexer instance for the optional input antlr.CharStream.
func NewCELLexer(input antlr.CharStream) *CELLexer {
CELLexerInit()
l := new(CELLexer)
l.BaseLexer = antlr.NewBaseLexer(input)
staticData := &CELLexerLexerStaticData
l.Interpreter = antlr.NewLexerATNSimulator(l, staticData.atn, staticData.decisionToDFA, staticData.PredictionContextCache)
l.channelNames = staticData.ChannelNames
l.modeNames = staticData.ModeNames
l.RuleNames = staticData.RuleNames
l.LiteralNames = staticData.LiteralNames
l.SymbolicNames = staticData.SymbolicNames
l.GrammarFileName = "CEL.g4"
// TODO: l.EOF = antlr.TokenEOF
return l
}
// CELLexer tokens.
const (
CELLexerEQUALS = 1
CELLexerNOT_EQUALS = 2
CELLexerIN = 3
CELLexerLESS = 4
CELLexerLESS_EQUALS = 5
CELLexerGREATER_EQUALS = 6
CELLexerGREATER = 7
CELLexerLOGICAL_AND = 8
CELLexerLOGICAL_OR = 9
CELLexerLBRACKET = 10
CELLexerRPRACKET = 11
CELLexerLBRACE = 12
CELLexerRBRACE = 13
CELLexerLPAREN = 14
CELLexerRPAREN = 15
CELLexerDOT = 16
CELLexerCOMMA = 17
CELLexerMINUS = 18
CELLexerEXCLAM = 19
CELLexerQUESTIONMARK = 20
CELLexerCOLON = 21
CELLexerPLUS = 22
CELLexerSTAR = 23
CELLexerSLASH = 24
CELLexerPERCENT = 25
CELLexerCEL_TRUE = 26
CELLexerCEL_FALSE = 27
CELLexerNUL = 28
CELLexerWHITESPACE = 29
CELLexerCOMMENT = 30
CELLexerNUM_FLOAT = 31
CELLexerNUM_INT = 32
CELLexerNUM_UINT = 33
CELLexerSTRING = 34
CELLexerBYTES = 35
CELLexerIDENTIFIER = 36
CELLexerESC_IDENTIFIER = 37
)
// Code generated from /usr/local/google/home/jdtatum/github/cel-go/parser/gen/CEL.g4 by ANTLR 4.13.1. DO NOT EDIT.
package gen // CEL
import (
"fmt"
"strconv"
"sync"
"github.com/antlr4-go/antlr/v4"
)
// Suppress unused import errors
var _ = fmt.Printf
var _ = strconv.Itoa
var _ = sync.Once{}
type CELParser struct {
*antlr.BaseParser
}
var CELParserStaticData struct {
once sync.Once
serializedATN []int32
LiteralNames []string
SymbolicNames []string
RuleNames []string
PredictionContextCache *antlr.PredictionContextCache
atn *antlr.ATN
decisionToDFA []*antlr.DFA
}
func celParserInit() {
staticData := &CELParserStaticData
staticData.LiteralNames = []string{
"", "'=='", "'!='", "'in'", "'<'", "'<='", "'>='", "'>'", "'&&'", "'||'",
"'['", "']'", "'{'", "'}'", "'('", "')'", "'.'", "','", "'-'", "'!'",
"'?'", "':'", "'+'", "'*'", "'/'", "'%'", "'true'", "'false'", "'null'",
}
staticData.SymbolicNames = []string{
"", "EQUALS", "NOT_EQUALS", "IN", "LESS", "LESS_EQUALS", "GREATER_EQUALS",
"GREATER", "LOGICAL_AND", "LOGICAL_OR", "LBRACKET", "RPRACKET", "LBRACE",
"RBRACE", "LPAREN", "RPAREN", "DOT", "COMMA", "MINUS", "EXCLAM", "QUESTIONMARK",
"COLON", "PLUS", "STAR", "SLASH", "PERCENT", "CEL_TRUE", "CEL_FALSE",
"NUL", "WHITESPACE", "COMMENT", "NUM_FLOAT", "NUM_INT", "NUM_UINT",
"STRING", "BYTES", "IDENTIFIER", "ESC_IDENTIFIER",
}
staticData.RuleNames = []string{
"start", "expr", "conditionalOr", "conditionalAnd", "relation", "calc",
"unary", "member", "primary", "exprList", "listInit", "fieldInitializerList",
"optField", "mapInitializerList", "escapeIdent", "optExpr", "literal",
}
staticData.PredictionContextCache = antlr.NewPredictionContextCache()
staticData.serializedATN = []int32{
4, 1, 37, 259, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2, 4, 7,
4, 2, 5, 7, 5, 2, 6, 7, 6, 2, 7, 7, 7, 2, 8, 7, 8, 2, 9, 7, 9, 2, 10, 7,
10, 2, 11, 7, 11, 2, 12, 7, 12, 2, 13, 7, 13, 2, 14, 7, 14, 2, 15, 7, 15,
2, 16, 7, 16, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3,
1, 44, 8, 1, 1, 2, 1, 2, 1, 2, 5, 2, 49, 8, 2, 10, 2, 12, 2, 52, 9, 2,
1, 3, 1, 3, 1, 3, 5, 3, 57, 8, 3, 10, 3, 12, 3, 60, 9, 3, 1, 4, 1, 4, 1,
4, 1, 4, 1, 4, 1, 4, 5, 4, 68, 8, 4, 10, 4, 12, 4, 71, 9, 4, 1, 5, 1, 5,
1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 5, 5, 82, 8, 5, 10, 5, 12, 5,
85, 9, 5, 1, 6, 1, 6, 4, 6, 89, 8, 6, 11, 6, 12, 6, 90, 1, 6, 1, 6, 4,
6, 95, 8, 6, 11, 6, 12, 6, 96, 1, 6, 3, 6, 100, 8, 6, 1, 7, 1, 7, 1, 7,
1, 7, 1, 7, 1, 7, 3, 7, 108, 8, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7,
3, 7, 116, 8, 7, 1, 7, 1, 7, 1, 7, 1, 7, 3, 7, 122, 8, 7, 1, 7, 1, 7, 1,
7, 5, 7, 127, 8, 7, 10, 7, 12, 7, 130, 9, 7, 1, 8, 3, 8, 133, 8, 8, 1,
8, 1, 8, 3, 8, 137, 8, 8, 1, 8, 1, 8, 1, 8, 3, 8, 142, 8, 8, 1, 8, 1, 8,
1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 3, 8, 151, 8, 8, 1, 8, 3, 8, 154, 8, 8, 1,
8, 1, 8, 1, 8, 3, 8, 159, 8, 8, 1, 8, 3, 8, 162, 8, 8, 1, 8, 1, 8, 3, 8,
166, 8, 8, 1, 8, 1, 8, 1, 8, 5, 8, 171, 8, 8, 10, 8, 12, 8, 174, 9, 8,
1, 8, 1, 8, 3, 8, 178, 8, 8, 1, 8, 3, 8, 181, 8, 8, 1, 8, 1, 8, 3, 8, 185,
8, 8, 1, 9, 1, 9, 1, 9, 5, 9, 190, 8, 9, 10, 9, 12, 9, 193, 9, 9, 1, 10,
1, 10, 1, 10, 5, 10, 198, 8, 10, 10, 10, 12, 10, 201, 9, 10, 1, 11, 1,
11, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 5, 11, 211, 8, 11, 10, 11,
12, 11, 214, 9, 11, 1, 12, 3, 12, 217, 8, 12, 1, 12, 1, 12, 1, 13, 1, 13,
1, 13, 1, 13, 1, 13, 1, 13, 1, 13, 1, 13, 5, 13, 229, 8, 13, 10, 13, 12,
13, 232, 9, 13, 1, 14, 1, 14, 3, 14, 236, 8, 14, 1, 15, 3, 15, 239, 8,
15, 1, 15, 1, 15, 1, 16, 3, 16, 244, 8, 16, 1, 16, 1, 16, 1, 16, 3, 16,
249, 8, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 3, 16, 257, 8, 16,
1, 16, 0, 3, 8, 10, 14, 17, 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22,
24, 26, 28, 30, 32, 0, 3, 1, 0, 1, 7, 1, 0, 23, 25, 2, 0, 18, 18, 22, 22,
290, 0, 34, 1, 0, 0, 0, 2, 37, 1, 0, 0, 0, 4, 45, 1, 0, 0, 0, 6, 53, 1,
0, 0, 0, 8, 61, 1, 0, 0, 0, 10, 72, 1, 0, 0, 0, 12, 99, 1, 0, 0, 0, 14,
101, 1, 0, 0, 0, 16, 184, 1, 0, 0, 0, 18, 186, 1, 0, 0, 0, 20, 194, 1,
0, 0, 0, 22, 202, 1, 0, 0, 0, 24, 216, 1, 0, 0, 0, 26, 220, 1, 0, 0, 0,
28, 235, 1, 0, 0, 0, 30, 238, 1, 0, 0, 0, 32, 256, 1, 0, 0, 0, 34, 35,
3, 2, 1, 0, 35, 36, 5, 0, 0, 1, 36, 1, 1, 0, 0, 0, 37, 43, 3, 4, 2, 0,
38, 39, 5, 20, 0, 0, 39, 40, 3, 4, 2, 0, 40, 41, 5, 21, 0, 0, 41, 42, 3,
2, 1, 0, 42, 44, 1, 0, 0, 0, 43, 38, 1, 0, 0, 0, 43, 44, 1, 0, 0, 0, 44,
3, 1, 0, 0, 0, 45, 50, 3, 6, 3, 0, 46, 47, 5, 9, 0, 0, 47, 49, 3, 6, 3,
0, 48, 46, 1, 0, 0, 0, 49, 52, 1, 0, 0, 0, 50, 48, 1, 0, 0, 0, 50, 51,
1, 0, 0, 0, 51, 5, 1, 0, 0, 0, 52, 50, 1, 0, 0, 0, 53, 58, 3, 8, 4, 0,
54, 55, 5, 8, 0, 0, 55, 57, 3, 8, 4, 0, 56, 54, 1, 0, 0, 0, 57, 60, 1,
0, 0, 0, 58, 56, 1, 0, 0, 0, 58, 59, 1, 0, 0, 0, 59, 7, 1, 0, 0, 0, 60,
58, 1, 0, 0, 0, 61, 62, 6, 4, -1, 0, 62, 63, 3, 10, 5, 0, 63, 69, 1, 0,
0, 0, 64, 65, 10, 1, 0, 0, 65, 66, 7, 0, 0, 0, 66, 68, 3, 8, 4, 2, 67,
64, 1, 0, 0, 0, 68, 71, 1, 0, 0, 0, 69, 67, 1, 0, 0, 0, 69, 70, 1, 0, 0,
0, 70, 9, 1, 0, 0, 0, 71, 69, 1, 0, 0, 0, 72, 73, 6, 5, -1, 0, 73, 74,
3, 12, 6, 0, 74, 83, 1, 0, 0, 0, 75, 76, 10, 2, 0, 0, 76, 77, 7, 1, 0,
0, 77, 82, 3, 10, 5, 3, 78, 79, 10, 1, 0, 0, 79, 80, 7, 2, 0, 0, 80, 82,
3, 10, 5, 2, 81, 75, 1, 0, 0, 0, 81, 78, 1, 0, 0, 0, 82, 85, 1, 0, 0, 0,
83, 81, 1, 0, 0, 0, 83, 84, 1, 0, 0, 0, 84, 11, 1, 0, 0, 0, 85, 83, 1,
0, 0, 0, 86, 100, 3, 14, 7, 0, 87, 89, 5, 19, 0, 0, 88, 87, 1, 0, 0, 0,
89, 90, 1, 0, 0, 0, 90, 88, 1, 0, 0, 0, 90, 91, 1, 0, 0, 0, 91, 92, 1,
0, 0, 0, 92, 100, 3, 14, 7, 0, 93, 95, 5, 18, 0, 0, 94, 93, 1, 0, 0, 0,
95, 96, 1, 0, 0, 0, 96, 94, 1, 0, 0, 0, 96, 97, 1, 0, 0, 0, 97, 98, 1,
0, 0, 0, 98, 100, 3, 14, 7, 0, 99, 86, 1, 0, 0, 0, 99, 88, 1, 0, 0, 0,
99, 94, 1, 0, 0, 0, 100, 13, 1, 0, 0, 0, 101, 102, 6, 7, -1, 0, 102, 103,
3, 16, 8, 0, 103, 128, 1, 0, 0, 0, 104, 105, 10, 3, 0, 0, 105, 107, 5,
16, 0, 0, 106, 108, 5, 20, 0, 0, 107, 106, 1, 0, 0, 0, 107, 108, 1, 0,
0, 0, 108, 109, 1, 0, 0, 0, 109, 127, 3, 28, 14, 0, 110, 111, 10, 2, 0,
0, 111, 112, 5, 16, 0, 0, 112, 113, 5, 36, 0, 0, 113, 115, 5, 14, 0, 0,
114, 116, 3, 18, 9, 0, 115, 114, 1, 0, 0, 0, 115, 116, 1, 0, 0, 0, 116,
117, 1, 0, 0, 0, 117, 127, 5, 15, 0, 0, 118, 119, 10, 1, 0, 0, 119, 121,
5, 10, 0, 0, 120, 122, 5, 20, 0, 0, 121, 120, 1, 0, 0, 0, 121, 122, 1,
0, 0, 0, 122, 123, 1, 0, 0, 0, 123, 124, 3, 2, 1, 0, 124, 125, 5, 11, 0,
0, 125, 127, 1, 0, 0, 0, 126, 104, 1, 0, 0, 0, 126, 110, 1, 0, 0, 0, 126,
118, 1, 0, 0, 0, 127, 130, 1, 0, 0, 0, 128, 126, 1, 0, 0, 0, 128, 129,
1, 0, 0, 0, 129, 15, 1, 0, 0, 0, 130, 128, 1, 0, 0, 0, 131, 133, 5, 16,
0, 0, 132, 131, 1, 0, 0, 0, 132, 133, 1, 0, 0, 0, 133, 134, 1, 0, 0, 0,
134, 185, 5, 36, 0, 0, 135, 137, 5, 16, 0, 0, 136, 135, 1, 0, 0, 0, 136,
137, 1, 0, 0, 0, 137, 138, 1, 0, 0, 0, 138, 139, 5, 36, 0, 0, 139, 141,
5, 14, 0, 0, 140, 142, 3, 18, 9, 0, 141, 140, 1, 0, 0, 0, 141, 142, 1,
0, 0, 0, 142, 143, 1, 0, 0, 0, 143, 185, 5, 15, 0, 0, 144, 145, 5, 14,
0, 0, 145, 146, 3, 2, 1, 0, 146, 147, 5, 15, 0, 0, 147, 185, 1, 0, 0, 0,
148, 150, 5, 10, 0, 0, 149, 151, 3, 20, 10, 0, 150, 149, 1, 0, 0, 0, 150,
151, 1, 0, 0, 0, 151, 153, 1, 0, 0, 0, 152, 154, 5, 17, 0, 0, 153, 152,
1, 0, 0, 0, 153, 154, 1, 0, 0, 0, 154, 155, 1, 0, 0, 0, 155, 185, 5, 11,
0, 0, 156, 158, 5, 12, 0, 0, 157, 159, 3, 26, 13, 0, 158, 157, 1, 0, 0,
0, 158, 159, 1, 0, 0, 0, 159, 161, 1, 0, 0, 0, 160, 162, 5, 17, 0, 0, 161,
160, 1, 0, 0, 0, 161, 162, 1, 0, 0, 0, 162, 163, 1, 0, 0, 0, 163, 185,
5, 13, 0, 0, 164, 166, 5, 16, 0, 0, 165, 164, 1, 0, 0, 0, 165, 166, 1,
0, 0, 0, 166, 167, 1, 0, 0, 0, 167, 172, 5, 36, 0, 0, 168, 169, 5, 16,
0, 0, 169, 171, 5, 36, 0, 0, 170, 168, 1, 0, 0, 0, 171, 174, 1, 0, 0, 0,
172, 170, 1, 0, 0, 0, 172, 173, 1, 0, 0, 0, 173, 175, 1, 0, 0, 0, 174,
172, 1, 0, 0, 0, 175, 177, 5, 12, 0, 0, 176, 178, 3, 22, 11, 0, 177, 176,
1, 0, 0, 0, 177, 178, 1, 0, 0, 0, 178, 180, 1, 0, 0, 0, 179, 181, 5, 17,
0, 0, 180, 179, 1, 0, 0, 0, 180, 181, 1, 0, 0, 0, 181, 182, 1, 0, 0, 0,
182, 185, 5, 13, 0, 0, 183, 185, 3, 32, 16, 0, 184, 132, 1, 0, 0, 0, 184,
136, 1, 0, 0, 0, 184, 144, 1, 0, 0, 0, 184, 148, 1, 0, 0, 0, 184, 156,
1, 0, 0, 0, 184, 165, 1, 0, 0, 0, 184, 183, 1, 0, 0, 0, 185, 17, 1, 0,
0, 0, 186, 191, 3, 2, 1, 0, 187, 188, 5, 17, 0, 0, 188, 190, 3, 2, 1, 0,
189, 187, 1, 0, 0, 0, 190, 193, 1, 0, 0, 0, 191, 189, 1, 0, 0, 0, 191,
192, 1, 0, 0, 0, 192, 19, 1, 0, 0, 0, 193, 191, 1, 0, 0, 0, 194, 199, 3,
30, 15, 0, 195, 196, 5, 17, 0, 0, 196, 198, 3, 30, 15, 0, 197, 195, 1,
0, 0, 0, 198, 201, 1, 0, 0, 0, 199, 197, 1, 0, 0, 0, 199, 200, 1, 0, 0,
0, 200, 21, 1, 0, 0, 0, 201, 199, 1, 0, 0, 0, 202, 203, 3, 24, 12, 0, 203,
204, 5, 21, 0, 0, 204, 212, 3, 2, 1, 0, 205, 206, 5, 17, 0, 0, 206, 207,
3, 24, 12, 0, 207, 208, 5, 21, 0, 0, 208, 209, 3, 2, 1, 0, 209, 211, 1,
0, 0, 0, 210, 205, 1, 0, 0, 0, 211, 214, 1, 0, 0, 0, 212, 210, 1, 0, 0,
0, 212, 213, 1, 0, 0, 0, 213, 23, 1, 0, 0, 0, 214, 212, 1, 0, 0, 0, 215,
217, 5, 20, 0, 0, 216, 215, 1, 0, 0, 0, 216, 217, 1, 0, 0, 0, 217, 218,
1, 0, 0, 0, 218, 219, 3, 28, 14, 0, 219, 25, 1, 0, 0, 0, 220, 221, 3, 30,
15, 0, 221, 222, 5, 21, 0, 0, 222, 230, 3, 2, 1, 0, 223, 224, 5, 17, 0,
0, 224, 225, 3, 30, 15, 0, 225, 226, 5, 21, 0, 0, 226, 227, 3, 2, 1, 0,
227, 229, 1, 0, 0, 0, 228, 223, 1, 0, 0, 0, 229, 232, 1, 0, 0, 0, 230,
228, 1, 0, 0, 0, 230, 231, 1, 0, 0, 0, 231, 27, 1, 0, 0, 0, 232, 230, 1,
0, 0, 0, 233, 236, 5, 36, 0, 0, 234, 236, 5, 37, 0, 0, 235, 233, 1, 0,
0, 0, 235, 234, 1, 0, 0, 0, 236, 29, 1, 0, 0, 0, 237, 239, 5, 20, 0, 0,
238, 237, 1, 0, 0, 0, 238, 239, 1, 0, 0, 0, 239, 240, 1, 0, 0, 0, 240,
241, 3, 2, 1, 0, 241, 31, 1, 0, 0, 0, 242, 244, 5, 18, 0, 0, 243, 242,
1, 0, 0, 0, 243, 244, 1, 0, 0, 0, 244, 245, 1, 0, 0, 0, 245, 257, 5, 32,
0, 0, 246, 257, 5, 33, 0, 0, 247, 249, 5, 18, 0, 0, 248, 247, 1, 0, 0,
0, 248, 249, 1, 0, 0, 0, 249, 250, 1, 0, 0, 0, 250, 257, 5, 31, 0, 0, 251,
257, 5, 34, 0, 0, 252, 257, 5, 35, 0, 0, 253, 257, 5, 26, 0, 0, 254, 257,
5, 27, 0, 0, 255, 257, 5, 28, 0, 0, 256, 243, 1, 0, 0, 0, 256, 246, 1,
0, 0, 0, 256, 248, 1, 0, 0, 0, 256, 251, 1, 0, 0, 0, 256, 252, 1, 0, 0,
0, 256, 253, 1, 0, 0, 0, 256, 254, 1, 0, 0, 0, 256, 255, 1, 0, 0, 0, 257,
33, 1, 0, 0, 0, 36, 43, 50, 58, 69, 81, 83, 90, 96, 99, 107, 115, 121,
126, 128, 132, 136, 141, 150, 153, 158, 161, 165, 172, 177, 180, 184, 191,
199, 212, 216, 230, 235, 238, 243, 248, 256,
}
deserializer := antlr.NewATNDeserializer(nil)
staticData.atn = deserializer.Deserialize(staticData.serializedATN)
atn := staticData.atn
staticData.decisionToDFA = make([]*antlr.DFA, len(atn.DecisionToState))
decisionToDFA := staticData.decisionToDFA
for index, state := range atn.DecisionToState {
decisionToDFA[index] = antlr.NewDFA(state, index)
}
}
// CELParserInit initializes any static state used to implement CELParser. By default the
// static state used to implement the parser is lazily initialized during the first call to
// NewCELParser(). You can call this function if you wish to initialize the static state ahead
// of time.
func CELParserInit() {
staticData := &CELParserStaticData
staticData.once.Do(celParserInit)
}
// NewCELParser produces a new parser instance for the optional input antlr.TokenStream.
func NewCELParser(input antlr.TokenStream) *CELParser {
CELParserInit()
this := new(CELParser)
this.BaseParser = antlr.NewBaseParser(input)
staticData := &CELParserStaticData
this.Interpreter = antlr.NewParserATNSimulator(this, staticData.atn, staticData.decisionToDFA, staticData.PredictionContextCache)
this.RuleNames = staticData.RuleNames
this.LiteralNames = staticData.LiteralNames
this.SymbolicNames = staticData.SymbolicNames
this.GrammarFileName = "CEL.g4"
return this
}
// CELParser tokens.
const (
CELParserEOF = antlr.TokenEOF
CELParserEQUALS = 1
CELParserNOT_EQUALS = 2
CELParserIN = 3
CELParserLESS = 4
CELParserLESS_EQUALS = 5
CELParserGREATER_EQUALS = 6
CELParserGREATER = 7
CELParserLOGICAL_AND = 8
CELParserLOGICAL_OR = 9
CELParserLBRACKET = 10
CELParserRPRACKET = 11
CELParserLBRACE = 12
CELParserRBRACE = 13
CELParserLPAREN = 14
CELParserRPAREN = 15
CELParserDOT = 16
CELParserCOMMA = 17
CELParserMINUS = 18
CELParserEXCLAM = 19
CELParserQUESTIONMARK = 20
CELParserCOLON = 21
CELParserPLUS = 22
CELParserSTAR = 23
CELParserSLASH = 24
CELParserPERCENT = 25
CELParserCEL_TRUE = 26
CELParserCEL_FALSE = 27
CELParserNUL = 28
CELParserWHITESPACE = 29
CELParserCOMMENT = 30
CELParserNUM_FLOAT = 31
CELParserNUM_INT = 32
CELParserNUM_UINT = 33
CELParserSTRING = 34
CELParserBYTES = 35
CELParserIDENTIFIER = 36
CELParserESC_IDENTIFIER = 37
)
// CELParser rules.
const (
CELParserRULE_start = 0
CELParserRULE_expr = 1
CELParserRULE_conditionalOr = 2
CELParserRULE_conditionalAnd = 3
CELParserRULE_relation = 4
CELParserRULE_calc = 5
CELParserRULE_unary = 6
CELParserRULE_member = 7
CELParserRULE_primary = 8
CELParserRULE_exprList = 9
CELParserRULE_listInit = 10
CELParserRULE_fieldInitializerList = 11
CELParserRULE_optField = 12
CELParserRULE_mapInitializerList = 13
CELParserRULE_escapeIdent = 14
CELParserRULE_optExpr = 15
CELParserRULE_literal = 16
)
// IStartContext is an interface to support dynamic dispatch.
type IStartContext interface {
antlr.ParserRuleContext
// GetParser returns the parser.
GetParser() antlr.Parser
// GetE returns the e rule contexts.
GetE() IExprContext
// SetE sets the e rule contexts.
SetE(IExprContext)
// Getter signatures
EOF() antlr.TerminalNode
Expr() IExprContext
// IsStartContext differentiates from other interfaces.
IsStartContext()
}
type StartContext struct {
antlr.BaseParserRuleContext
parser antlr.Parser
e IExprContext
}
func NewEmptyStartContext() *StartContext {
var p = new(StartContext)
antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1)
p.RuleIndex = CELParserRULE_start
return p
}
func InitEmptyStartContext(p *StartContext) {
antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1)
p.RuleIndex = CELParserRULE_start
}
func (*StartContext) IsStartContext() {}
func NewStartContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *StartContext {
var p = new(StartContext)
antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, parent, invokingState)
p.parser = parser
p.RuleIndex = CELParserRULE_start
return p
}
func (s *StartContext) GetParser() antlr.Parser { return s.parser }
func (s *StartContext) GetE() IExprContext { return s.e }
func (s *StartContext) SetE(v IExprContext) { s.e = v }
func (s *StartContext) EOF() antlr.TerminalNode {
return s.GetToken(CELParserEOF, 0)
}
func (s *StartContext) Expr() IExprContext {
var t antlr.RuleContext
for _, ctx := range s.GetChildren() {
if _, ok := ctx.(IExprContext); ok {
t = ctx.(antlr.RuleContext)
break
}
}
if t == nil {
return nil
}
return t.(IExprContext)
}
func (s *StartContext) GetRuleContext() antlr.RuleContext {
return s
}
func (s *StartContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string {
return antlr.TreesStringTree(s, ruleNames, recog)
}
func (s *StartContext) EnterRule(listener antlr.ParseTreeListener) {
if listenerT, ok := listener.(CELListener); ok {
listenerT.EnterStart(s)
}
}
func (s *StartContext) ExitRule(listener antlr.ParseTreeListener) {
if listenerT, ok := listener.(CELListener); ok {
listenerT.ExitStart(s)
}
}
func (s *StartContext) Accept(visitor antlr.ParseTreeVisitor) interface{} {
switch t := visitor.(type) {
case CELVisitor:
return t.VisitStart(s)
default:
return t.VisitChildren(s)
}
}
func (p *CELParser) Start_() (localctx IStartContext) {
localctx = NewStartContext(p, p.GetParserRuleContext(), p.GetState())
p.EnterRule(localctx, 0, CELParserRULE_start)
p.EnterOuterAlt(localctx, 1)
{
p.SetState(34)
var _x = p.Expr()
localctx.(*StartContext).e = _x
}
{
p.SetState(35)
p.Match(CELParserEOF)
if p.HasError() {
// Recognition error - abort rule
goto errorExit
}
}
errorExit:
if p.HasError() {
v := p.GetError()
localctx.SetException(v)
p.GetErrorHandler().ReportError(p, v)
p.GetErrorHandler().Recover(p, v)
p.SetError(nil)
}
p.ExitRule()
return localctx
goto errorExit // Trick to prevent compiler error if the label is not used
}
// IExprContext is an interface to support dynamic dispatch.
type IExprContext interface {
antlr.ParserRuleContext
// GetParser returns the parser.
GetParser() antlr.Parser
// GetOp returns the op token.
GetOp() antlr.Token
// SetOp sets the op token.
SetOp(antlr.Token)
// GetE returns the e rule contexts.
GetE() IConditionalOrContext
// GetE1 returns the e1 rule contexts.
GetE1() IConditionalOrContext
// GetE2 returns the e2 rule contexts.
GetE2() IExprContext
// SetE sets the e rule contexts.
SetE(IConditionalOrContext)
// SetE1 sets the e1 rule contexts.
SetE1(IConditionalOrContext)
// SetE2 sets the e2 rule contexts.
SetE2(IExprContext)
// Getter signatures
AllConditionalOr() []IConditionalOrContext
ConditionalOr(i int) IConditionalOrContext
COLON() antlr.TerminalNode
QUESTIONMARK() antlr.TerminalNode
Expr() IExprContext
// IsExprContext differentiates from other interfaces.
IsExprContext()
}
type ExprContext struct {
antlr.BaseParserRuleContext
parser antlr.Parser
e IConditionalOrContext
op antlr.Token
e1 IConditionalOrContext
e2 IExprContext
}
func NewEmptyExprContext() *ExprContext {
var p = new(ExprContext)
antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1)
p.RuleIndex = CELParserRULE_expr
return p
}
func InitEmptyExprContext(p *ExprContext) {
antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1)
p.RuleIndex = CELParserRULE_expr
}
func (*ExprContext) IsExprContext() {}
func NewExprContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *ExprContext {
var p = new(ExprContext)
antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, parent, invokingState)
p.parser = parser
p.RuleIndex = CELParserRULE_expr
return p
}
func (s *ExprContext) GetParser() antlr.Parser { return s.parser }
func (s *ExprContext) GetOp() antlr.Token { return s.op }
func (s *ExprContext) SetOp(v antlr.Token) { s.op = v }
func (s *ExprContext) GetE() IConditionalOrContext { return s.e }
func (s *ExprContext) GetE1() IConditionalOrContext { return s.e1 }
func (s *ExprContext) GetE2() IExprContext { return s.e2 }
func (s *ExprContext) SetE(v IConditionalOrContext) { s.e = v }
func (s *ExprContext) SetE1(v IConditionalOrContext) { s.e1 = v }
func (s *ExprContext) SetE2(v IExprContext) { s.e2 = v }
func (s *ExprContext) AllConditionalOr() []IConditionalOrContext {
children := s.GetChildren()
len := 0
for _, ctx := range children {
if _, ok := ctx.(IConditionalOrContext); ok {
len++
}
}
tst := make([]IConditionalOrContext, len)
i := 0
for _, ctx := range children {
if t, ok := ctx.(IConditionalOrContext); ok {
tst[i] = t.(IConditionalOrContext)
i++
}
}
return tst
}
func (s *ExprContext) ConditionalOr(i int) IConditionalOrContext {
var t antlr.RuleContext
j := 0
for _, ctx := range s.GetChildren() {
if _, ok := ctx.(IConditionalOrContext); ok {
if j == i {
t = ctx.(antlr.RuleContext)
break
}
j++
}
}
if t == nil {
return nil
}
return t.(IConditionalOrContext)
}
func (s *ExprContext) COLON() antlr.TerminalNode {
return s.GetToken(CELParserCOLON, 0)
}
func (s *ExprContext) QUESTIONMARK() antlr.TerminalNode {
return s.GetToken(CELParserQUESTIONMARK, 0)
}
func (s *ExprContext) Expr() IExprContext {
var t antlr.RuleContext
for _, ctx := range s.GetChildren() {
if _, ok := ctx.(IExprContext); ok {
t = ctx.(antlr.RuleContext)
break
}
}
if t == nil {
return nil
}
return t.(IExprContext)
}
func (s *ExprContext) GetRuleContext() antlr.RuleContext {
return s
}
func (s *ExprContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string {
return antlr.TreesStringTree(s, ruleNames, recog)
}
func (s *ExprContext) EnterRule(listener antlr.ParseTreeListener) {
if listenerT, ok := listener.(CELListener); ok {
listenerT.EnterExpr(s)
}
}
func (s *ExprContext) ExitRule(listener antlr.ParseTreeListener) {
if listenerT, ok := listener.(CELListener); ok {
listenerT.ExitExpr(s)
}
}
func (s *ExprContext) Accept(visitor antlr.ParseTreeVisitor) interface{} {
switch t := visitor.(type) {
case CELVisitor:
return t.VisitExpr(s)
default:
return t.VisitChildren(s)
}
}
func (p *CELParser) Expr() (localctx IExprContext) {
localctx = NewExprContext(p, p.GetParserRuleContext(), p.GetState())
p.EnterRule(localctx, 2, CELParserRULE_expr)
var _la int
p.EnterOuterAlt(localctx, 1)
{
p.SetState(37)
var _x = p.ConditionalOr()
localctx.(*ExprContext).e = _x
}
p.SetState(43)
p.GetErrorHandler().Sync(p)
if p.HasError() {
goto errorExit
}
_la = p.GetTokenStream().LA(1)
if _la == CELParserQUESTIONMARK {
{
p.SetState(38)
var _m = p.Match(CELParserQUESTIONMARK)
localctx.(*ExprContext).op = _m
if p.HasError() {
// Recognition error - abort rule
goto errorExit
}
}
{
p.SetState(39)
var _x = p.ConditionalOr()
localctx.(*ExprContext).e1 = _x
}
{
p.SetState(40)
p.Match(CELParserCOLON)
if p.HasError() {
// Recognition error - abort rule
goto errorExit
}
}
{
p.SetState(41)
var _x = p.Expr()
localctx.(*ExprContext).e2 = _x
}
}
errorExit:
if p.HasError() {
v := p.GetError()
localctx.SetException(v)
p.GetErrorHandler().ReportError(p, v)
p.GetErrorHandler().Recover(p, v)
p.SetError(nil)
}
p.ExitRule()
return localctx
goto errorExit // Trick to prevent compiler error if the label is not used
}
// IConditionalOrContext is an interface to support dynamic dispatch.
type IConditionalOrContext interface {
antlr.ParserRuleContext
// GetParser returns the parser.
GetParser() antlr.Parser
// GetS9 returns the s9 token.
GetS9() antlr.Token
// SetS9 sets the s9 token.
SetS9(antlr.Token)
// GetOps returns the ops token list.
GetOps() []antlr.Token
// SetOps sets the ops token list.
SetOps([]antlr.Token)
// GetE returns the e rule contexts.
GetE() IConditionalAndContext
// Get_conditionalAnd returns the _conditionalAnd rule contexts.
Get_conditionalAnd() IConditionalAndContext
// SetE sets the e rule contexts.
SetE(IConditionalAndContext)
// Set_conditionalAnd sets the _conditionalAnd rule contexts.
Set_conditionalAnd(IConditionalAndContext)
// GetE1 returns the e1 rule context list.
GetE1() []IConditionalAndContext
// SetE1 sets the e1 rule context list.
SetE1([]IConditionalAndContext)
// Getter signatures
AllConditionalAnd() []IConditionalAndContext
ConditionalAnd(i int) IConditionalAndContext
AllLOGICAL_OR() []antlr.TerminalNode
LOGICAL_OR(i int) antlr.TerminalNode
// IsConditionalOrContext differentiates from other interfaces.
IsConditionalOrContext()
}
type ConditionalOrContext struct {
antlr.BaseParserRuleContext
parser antlr.Parser
e IConditionalAndContext
s9 antlr.Token
ops []antlr.Token
_conditionalAnd IConditionalAndContext
e1 []IConditionalAndContext
}
func NewEmptyConditionalOrContext() *ConditionalOrContext {
var p = new(ConditionalOrContext)
antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1)
p.RuleIndex = CELParserRULE_conditionalOr
return p
}
func InitEmptyConditionalOrContext(p *ConditionalOrContext) {
antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1)
p.RuleIndex = CELParserRULE_conditionalOr
}
func (*ConditionalOrContext) IsConditionalOrContext() {}
func NewConditionalOrContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *ConditionalOrContext {
var p = new(ConditionalOrContext)
antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, parent, invokingState)
p.parser = parser
p.RuleIndex = CELParserRULE_conditionalOr
return p
}
func (s *ConditionalOrContext) GetParser() antlr.Parser { return s.parser }
func (s *ConditionalOrContext) GetS9() antlr.Token { return s.s9 }
func (s *ConditionalOrContext) SetS9(v antlr.Token) { s.s9 = v }
func (s *ConditionalOrContext) GetOps() []antlr.Token { return s.ops }
func (s *ConditionalOrContext) SetOps(v []antlr.Token) { s.ops = v }
func (s *ConditionalOrContext) GetE() IConditionalAndContext { return s.e }
func (s *ConditionalOrContext) Get_conditionalAnd() IConditionalAndContext { return s._conditionalAnd }
func (s *ConditionalOrContext) SetE(v IConditionalAndContext) { s.e = v }
func (s *ConditionalOrContext) Set_conditionalAnd(v IConditionalAndContext) { s._conditionalAnd = v }
func (s *ConditionalOrContext) GetE1() []IConditionalAndContext { return s.e1 }
func (s *ConditionalOrContext) SetE1(v []IConditionalAndContext) { s.e1 = v }
func (s *ConditionalOrContext) AllConditionalAnd() []IConditionalAndContext {
children := s.GetChildren()
len := 0
for _, ctx := range children {
if _, ok := ctx.(IConditionalAndContext); ok {
len++
}
}
tst := make([]IConditionalAndContext, len)
i := 0
for _, ctx := range children {
if t, ok := ctx.(IConditionalAndContext); ok {
tst[i] = t.(IConditionalAndContext)
i++
}
}
return tst
}
func (s *ConditionalOrContext) ConditionalAnd(i int) IConditionalAndContext {
var t antlr.RuleContext
j := 0
for _, ctx := range s.GetChildren() {
if _, ok := ctx.(IConditionalAndContext); ok {
if j == i {
t = ctx.(antlr.RuleContext)
break
}
j++
}
}
if t == nil {
return nil
}
return t.(IConditionalAndContext)
}
func (s *ConditionalOrContext) AllLOGICAL_OR() []antlr.TerminalNode {
return s.GetTokens(CELParserLOGICAL_OR)
}
func (s *ConditionalOrContext) LOGICAL_OR(i int) antlr.TerminalNode {
return s.GetToken(CELParserLOGICAL_OR, i)
}
func (s *ConditionalOrContext) GetRuleContext() antlr.RuleContext {
return s
}
func (s *ConditionalOrContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string {
return antlr.TreesStringTree(s, ruleNames, recog)
}
func (s *ConditionalOrContext) EnterRule(listener antlr.ParseTreeListener) {
if listenerT, ok := listener.(CELListener); ok {
listenerT.EnterConditionalOr(s)
}
}
func (s *ConditionalOrContext) ExitRule(listener antlr.ParseTreeListener) {
if listenerT, ok := listener.(CELListener); ok {
listenerT.ExitConditionalOr(s)
}
}
func (s *ConditionalOrContext) Accept(visitor antlr.ParseTreeVisitor) interface{} {
switch t := visitor.(type) {
case CELVisitor:
return t.VisitConditionalOr(s)
default:
return t.VisitChildren(s)
}
}
func (p *CELParser) ConditionalOr() (localctx IConditionalOrContext) {
localctx = NewConditionalOrContext(p, p.GetParserRuleContext(), p.GetState())
p.EnterRule(localctx, 4, CELParserRULE_conditionalOr)
var _la int
p.EnterOuterAlt(localctx, 1)
{
p.SetState(45)
var _x = p.ConditionalAnd()
localctx.(*ConditionalOrContext).e = _x
}
p.SetState(50)
p.GetErrorHandler().Sync(p)
if p.HasError() {
goto errorExit
}
_la = p.GetTokenStream().LA(1)
for _la == CELParserLOGICAL_OR {
{
p.SetState(46)
var _m = p.Match(CELParserLOGICAL_OR)
localctx.(*ConditionalOrContext).s9 = _m
if p.HasError() {
// Recognition error - abort rule
goto errorExit
}
}
localctx.(*ConditionalOrContext).ops = append(localctx.(*ConditionalOrContext).ops, localctx.(*ConditionalOrContext).s9)
{
p.SetState(47)
var _x = p.ConditionalAnd()
localctx.(*ConditionalOrContext)._conditionalAnd = _x
}
localctx.(*ConditionalOrContext).e1 = append(localctx.(*ConditionalOrContext).e1, localctx.(*ConditionalOrContext)._conditionalAnd)
p.SetState(52)
p.GetErrorHandler().Sync(p)
if p.HasError() {
goto errorExit
}
_la = p.GetTokenStream().LA(1)
}
errorExit:
if p.HasError() {
v := p.GetError()
localctx.SetException(v)
p.GetErrorHandler().ReportError(p, v)
p.GetErrorHandler().Recover(p, v)
p.SetError(nil)
}
p.ExitRule()
return localctx
goto errorExit // Trick to prevent compiler error if the label is not used
}
// IConditionalAndContext is an interface to support dynamic dispatch.
type IConditionalAndContext interface {
antlr.ParserRuleContext
// GetParser returns the parser.
GetParser() antlr.Parser
// GetS8 returns the s8 token.
GetS8() antlr.Token
// SetS8 sets the s8 token.
SetS8(antlr.Token)
// GetOps returns the ops token list.
GetOps() []antlr.Token
// SetOps sets the ops token list.
SetOps([]antlr.Token)
// GetE returns the e rule contexts.
GetE() IRelationContext
// Get_relation returns the _relation rule contexts.
Get_relation() IRelationContext
// SetE sets the e rule contexts.
SetE(IRelationContext)
// Set_relation sets the _relation rule contexts.
Set_relation(IRelationContext)
// GetE1 returns the e1 rule context list.
GetE1() []IRelationContext
// SetE1 sets the e1 rule context list.
SetE1([]IRelationContext)
// Getter signatures
AllRelation() []IRelationContext
Relation(i int) IRelationContext
AllLOGICAL_AND() []antlr.TerminalNode
LOGICAL_AND(i int) antlr.TerminalNode
// IsConditionalAndContext differentiates from other interfaces.
IsConditionalAndContext()
}
type ConditionalAndContext struct {
antlr.BaseParserRuleContext
parser antlr.Parser
e IRelationContext
s8 antlr.Token
ops []antlr.Token
_relation IRelationContext
e1 []IRelationContext
}
func NewEmptyConditionalAndContext() *ConditionalAndContext {
var p = new(ConditionalAndContext)
antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1)
p.RuleIndex = CELParserRULE_conditionalAnd
return p
}
func InitEmptyConditionalAndContext(p *ConditionalAndContext) {
antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1)
p.RuleIndex = CELParserRULE_conditionalAnd
}
func (*ConditionalAndContext) IsConditionalAndContext() {}
func NewConditionalAndContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *ConditionalAndContext {
var p = new(ConditionalAndContext)
antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, parent, invokingState)
p.parser = parser
p.RuleIndex = CELParserRULE_conditionalAnd
return p
}
func (s *ConditionalAndContext) GetParser() antlr.Parser { return s.parser }
func (s *ConditionalAndContext) GetS8() antlr.Token { return s.s8 }
func (s *ConditionalAndContext) SetS8(v antlr.Token) { s.s8 = v }
func (s *ConditionalAndContext) GetOps() []antlr.Token { return s.ops }
func (s *ConditionalAndContext) SetOps(v []antlr.Token) { s.ops = v }
func (s *ConditionalAndContext) GetE() IRelationContext { return s.e }
func (s *ConditionalAndContext) Get_relation() IRelationContext { return s._relation }
func (s *ConditionalAndContext) SetE(v IRelationContext) { s.e = v }
func (s *ConditionalAndContext) Set_relation(v IRelationContext) { s._relation = v }
func (s *ConditionalAndContext) GetE1() []IRelationContext { return s.e1 }
func (s *ConditionalAndContext) SetE1(v []IRelationContext) { s.e1 = v }
func (s *ConditionalAndContext) AllRelation() []IRelationContext {
children := s.GetChildren()
len := 0
for _, ctx := range children {
if _, ok := ctx.(IRelationContext); ok {
len++
}
}
tst := make([]IRelationContext, len)
i := 0
for _, ctx := range children {
if t, ok := ctx.(IRelationContext); ok {
tst[i] = t.(IRelationContext)
i++
}
}
return tst
}
func (s *ConditionalAndContext) Relation(i int) IRelationContext {
var t antlr.RuleContext
j := 0
for _, ctx := range s.GetChildren() {
if _, ok := ctx.(IRelationContext); ok {
if j == i {
t = ctx.(antlr.RuleContext)
break
}
j++
}
}
if t == nil {
return nil
}
return t.(IRelationContext)
}
func (s *ConditionalAndContext) AllLOGICAL_AND() []antlr.TerminalNode {
return s.GetTokens(CELParserLOGICAL_AND)
}
func (s *ConditionalAndContext) LOGICAL_AND(i int) antlr.TerminalNode {
return s.GetToken(CELParserLOGICAL_AND, i)
}
func (s *ConditionalAndContext) GetRuleContext() antlr.RuleContext {
return s
}
func (s *ConditionalAndContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string {
return antlr.TreesStringTree(s, ruleNames, recog)
}
func (s *ConditionalAndContext) EnterRule(listener antlr.ParseTreeListener) {
if listenerT, ok := listener.(CELListener); ok {
listenerT.EnterConditionalAnd(s)
}
}
func (s *ConditionalAndContext) ExitRule(listener antlr.ParseTreeListener) {
if listenerT, ok := listener.(CELListener); ok {
listenerT.ExitConditionalAnd(s)
}
}
func (s *ConditionalAndContext) Accept(visitor antlr.ParseTreeVisitor) interface{} {
switch t := visitor.(type) {
case CELVisitor:
return t.VisitConditionalAnd(s)
default:
return t.VisitChildren(s)
}
}
func (p *CELParser) ConditionalAnd() (localctx IConditionalAndContext) {
localctx = NewConditionalAndContext(p, p.GetParserRuleContext(), p.GetState())
p.EnterRule(localctx, 6, CELParserRULE_conditionalAnd)
var _la int
p.EnterOuterAlt(localctx, 1)
{
p.SetState(53)
var _x = p.relation(0)
localctx.(*ConditionalAndContext).e = _x
}
p.SetState(58)
p.GetErrorHandler().Sync(p)
if p.HasError() {
goto errorExit
}
_la = p.GetTokenStream().LA(1)
for _la == CELParserLOGICAL_AND {
{
p.SetState(54)
var _m = p.Match(CELParserLOGICAL_AND)
localctx.(*ConditionalAndContext).s8 = _m
if p.HasError() {
// Recognition error - abort rule
goto errorExit
}
}
localctx.(*ConditionalAndContext).ops = append(localctx.(*ConditionalAndContext).ops, localctx.(*ConditionalAndContext).s8)
{
p.SetState(55)
var _x = p.relation(0)
localctx.(*ConditionalAndContext)._relation = _x
}
localctx.(*ConditionalAndContext).e1 = append(localctx.(*ConditionalAndContext).e1, localctx.(*ConditionalAndContext)._relation)
p.SetState(60)
p.GetErrorHandler().Sync(p)
if p.HasError() {
goto errorExit
}
_la = p.GetTokenStream().LA(1)
}
errorExit:
if p.HasError() {
v := p.GetError()
localctx.SetException(v)
p.GetErrorHandler().ReportError(p, v)
p.GetErrorHandler().Recover(p, v)
p.SetError(nil)
}
p.ExitRule()
return localctx
goto errorExit // Trick to prevent compiler error if the label is not used
}
// IRelationContext is an interface to support dynamic dispatch.
type IRelationContext interface {
antlr.ParserRuleContext
// GetParser returns the parser.
GetParser() antlr.Parser
// GetOp returns the op token.
GetOp() antlr.Token
// SetOp sets the op token.
SetOp(antlr.Token)
// Getter signatures
Calc() ICalcContext
AllRelation() []IRelationContext
Relation(i int) IRelationContext
LESS() antlr.TerminalNode
LESS_EQUALS() antlr.TerminalNode
GREATER_EQUALS() antlr.TerminalNode
GREATER() antlr.TerminalNode
EQUALS() antlr.TerminalNode
NOT_EQUALS() antlr.TerminalNode
IN() antlr.TerminalNode
// IsRelationContext differentiates from other interfaces.
IsRelationContext()
}
type RelationContext struct {
antlr.BaseParserRuleContext
parser antlr.Parser
op antlr.Token
}
func NewEmptyRelationContext() *RelationContext {
var p = new(RelationContext)
antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1)
p.RuleIndex = CELParserRULE_relation
return p
}
func InitEmptyRelationContext(p *RelationContext) {
antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1)
p.RuleIndex = CELParserRULE_relation
}
func (*RelationContext) IsRelationContext() {}
func NewRelationContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *RelationContext {
var p = new(RelationContext)
antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, parent, invokingState)
p.parser = parser
p.RuleIndex = CELParserRULE_relation
return p
}
func (s *RelationContext) GetParser() antlr.Parser { return s.parser }
func (s *RelationContext) GetOp() antlr.Token { return s.op }
func (s *RelationContext) SetOp(v antlr.Token) { s.op = v }
func (s *RelationContext) Calc() ICalcContext {
var t antlr.RuleContext
for _, ctx := range s.GetChildren() {
if _, ok := ctx.(ICalcContext); ok {
t = ctx.(antlr.RuleContext)
break
}
}
if t == nil {
return nil
}
return t.(ICalcContext)
}
func (s *RelationContext) AllRelation() []IRelationContext {
children := s.GetChildren()
len := 0
for _, ctx := range children {
if _, ok := ctx.(IRelationContext); ok {
len++
}
}
tst := make([]IRelationContext, len)
i := 0
for _, ctx := range children {
if t, ok := ctx.(IRelationContext); ok {
tst[i] = t.(IRelationContext)
i++
}
}
return tst
}
func (s *RelationContext) Relation(i int) IRelationContext {
var t antlr.RuleContext
j := 0
for _, ctx := range s.GetChildren() {
if _, ok := ctx.(IRelationContext); ok {
if j == i {
t = ctx.(antlr.RuleContext)
break
}
j++
}
}
if t == nil {
return nil
}
return t.(IRelationContext)
}
func (s *RelationContext) LESS() antlr.TerminalNode {
return s.GetToken(CELParserLESS, 0)
}
func (s *RelationContext) LESS_EQUALS() antlr.TerminalNode {
return s.GetToken(CELParserLESS_EQUALS, 0)
}
func (s *RelationContext) GREATER_EQUALS() antlr.TerminalNode {
return s.GetToken(CELParserGREATER_EQUALS, 0)
}
func (s *RelationContext) GREATER() antlr.TerminalNode {
return s.GetToken(CELParserGREATER, 0)
}
func (s *RelationContext) EQUALS() antlr.TerminalNode {
return s.GetToken(CELParserEQUALS, 0)
}
func (s *RelationContext) NOT_EQUALS() antlr.TerminalNode {
return s.GetToken(CELParserNOT_EQUALS, 0)
}
func (s *RelationContext) IN() antlr.TerminalNode {
return s.GetToken(CELParserIN, 0)
}
func (s *RelationContext) GetRuleContext() antlr.RuleContext {
return s
}
func (s *RelationContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string {
return antlr.TreesStringTree(s, ruleNames, recog)
}
func (s *RelationContext) EnterRule(listener antlr.ParseTreeListener) {
if listenerT, ok := listener.(CELListener); ok {
listenerT.EnterRelation(s)
}
}
func (s *RelationContext) ExitRule(listener antlr.ParseTreeListener) {
if listenerT, ok := listener.(CELListener); ok {
listenerT.ExitRelation(s)
}
}
func (s *RelationContext) Accept(visitor antlr.ParseTreeVisitor) interface{} {
switch t := visitor.(type) {
case CELVisitor:
return t.VisitRelation(s)
default:
return t.VisitChildren(s)
}
}
func (p *CELParser) Relation() (localctx IRelationContext) {
return p.relation(0)
}
func (p *CELParser) relation(_p int) (localctx IRelationContext) {
var _parentctx antlr.ParserRuleContext = p.GetParserRuleContext()
_parentState := p.GetState()
localctx = NewRelationContext(p, p.GetParserRuleContext(), _parentState)
var _prevctx IRelationContext = localctx
var _ antlr.ParserRuleContext = _prevctx // TODO: To prevent unused variable warning.
_startState := 8
p.EnterRecursionRule(localctx, 8, CELParserRULE_relation, _p)
var _la int
var _alt int
p.EnterOuterAlt(localctx, 1)
{
p.SetState(62)
p.calc(0)
}
p.GetParserRuleContext().SetStop(p.GetTokenStream().LT(-1))
p.SetState(69)
p.GetErrorHandler().Sync(p)
if p.HasError() {
goto errorExit
}
_alt = p.GetInterpreter().AdaptivePredict(p.BaseParser, p.GetTokenStream(), 3, p.GetParserRuleContext())
if p.HasError() {
goto errorExit
}
for _alt != 2 && _alt != antlr.ATNInvalidAltNumber {
if _alt == 1 {
if p.GetParseListeners() != nil {
p.TriggerExitRuleEvent()
}
_prevctx = localctx
localctx = NewRelationContext(p, _parentctx, _parentState)
p.PushNewRecursionContext(localctx, _startState, CELParserRULE_relation)
p.SetState(64)
if !(p.Precpred(p.GetParserRuleContext(), 1)) {
p.SetError(antlr.NewFailedPredicateException(p, "p.Precpred(p.GetParserRuleContext(), 1)", ""))
goto errorExit
}
{
p.SetState(65)
var _lt = p.GetTokenStream().LT(1)
localctx.(*RelationContext).op = _lt
_la = p.GetTokenStream().LA(1)
if !((int64(_la) & ^0x3f) == 0 && ((int64(1)<<_la)&254) != 0) {
var _ri = p.GetErrorHandler().RecoverInline(p)
localctx.(*RelationContext).op = _ri
} else {
p.GetErrorHandler().ReportMatch(p)
p.Consume()
}
}
{
p.SetState(66)
p.relation(2)
}
}
p.SetState(71)
p.GetErrorHandler().Sync(p)
if p.HasError() {
goto errorExit
}
_alt = p.GetInterpreter().AdaptivePredict(p.BaseParser, p.GetTokenStream(), 3, p.GetParserRuleContext())
if p.HasError() {
goto errorExit
}
}
errorExit:
if p.HasError() {
v := p.GetError()
localctx.SetException(v)
p.GetErrorHandler().ReportError(p, v)
p.GetErrorHandler().Recover(p, v)
p.SetError(nil)
}
p.UnrollRecursionContexts(_parentctx)
return localctx
goto errorExit // Trick to prevent compiler error if the label is not used
}
// ICalcContext is an interface to support dynamic dispatch.
type ICalcContext interface {
antlr.ParserRuleContext
// GetParser returns the parser.
GetParser() antlr.Parser
// GetOp returns the op token.
GetOp() antlr.Token
// SetOp sets the op token.
SetOp(antlr.Token)
// Getter signatures
Unary() IUnaryContext
AllCalc() []ICalcContext
Calc(i int) ICalcContext
STAR() antlr.TerminalNode
SLASH() antlr.TerminalNode
PERCENT() antlr.TerminalNode
PLUS() antlr.TerminalNode
MINUS() antlr.TerminalNode
// IsCalcContext differentiates from other interfaces.
IsCalcContext()
}
type CalcContext struct {
antlr.BaseParserRuleContext
parser antlr.Parser
op antlr.Token
}
func NewEmptyCalcContext() *CalcContext {
var p = new(CalcContext)
antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1)
p.RuleIndex = CELParserRULE_calc
return p
}
func InitEmptyCalcContext(p *CalcContext) {
antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1)
p.RuleIndex = CELParserRULE_calc
}
func (*CalcContext) IsCalcContext() {}
func NewCalcContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *CalcContext {
var p = new(CalcContext)
antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, parent, invokingState)
p.parser = parser
p.RuleIndex = CELParserRULE_calc
return p
}
func (s *CalcContext) GetParser() antlr.Parser { return s.parser }
func (s *CalcContext) GetOp() antlr.Token { return s.op }
func (s *CalcContext) SetOp(v antlr.Token) { s.op = v }
func (s *CalcContext) Unary() IUnaryContext {
var t antlr.RuleContext
for _, ctx := range s.GetChildren() {
if _, ok := ctx.(IUnaryContext); ok {
t = ctx.(antlr.RuleContext)
break
}
}
if t == nil {
return nil
}
return t.(IUnaryContext)
}
func (s *CalcContext) AllCalc() []ICalcContext {
children := s.GetChildren()
len := 0
for _, ctx := range children {
if _, ok := ctx.(ICalcContext); ok {
len++
}
}
tst := make([]ICalcContext, len)
i := 0
for _, ctx := range children {
if t, ok := ctx.(ICalcContext); ok {
tst[i] = t.(ICalcContext)
i++
}
}
return tst
}
func (s *CalcContext) Calc(i int) ICalcContext {
var t antlr.RuleContext
j := 0
for _, ctx := range s.GetChildren() {
if _, ok := ctx.(ICalcContext); ok {
if j == i {
t = ctx.(antlr.RuleContext)
break
}
j++
}
}
if t == nil {
return nil
}
return t.(ICalcContext)
}
func (s *CalcContext) STAR() antlr.TerminalNode {
return s.GetToken(CELParserSTAR, 0)
}
func (s *CalcContext) SLASH() antlr.TerminalNode {
return s.GetToken(CELParserSLASH, 0)
}
func (s *CalcContext) PERCENT() antlr.TerminalNode {
return s.GetToken(CELParserPERCENT, 0)
}
func (s *CalcContext) PLUS() antlr.TerminalNode {
return s.GetToken(CELParserPLUS, 0)
}
func (s *CalcContext) MINUS() antlr.TerminalNode {
return s.GetToken(CELParserMINUS, 0)
}
func (s *CalcContext) GetRuleContext() antlr.RuleContext {
return s
}
func (s *CalcContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string {
return antlr.TreesStringTree(s, ruleNames, recog)
}
func (s *CalcContext) EnterRule(listener antlr.ParseTreeListener) {
if listenerT, ok := listener.(CELListener); ok {
listenerT.EnterCalc(s)
}
}
func (s *CalcContext) ExitRule(listener antlr.ParseTreeListener) {
if listenerT, ok := listener.(CELListener); ok {
listenerT.ExitCalc(s)
}
}
func (s *CalcContext) Accept(visitor antlr.ParseTreeVisitor) interface{} {
switch t := visitor.(type) {
case CELVisitor:
return t.VisitCalc(s)
default:
return t.VisitChildren(s)
}
}
func (p *CELParser) Calc() (localctx ICalcContext) {
return p.calc(0)
}
func (p *CELParser) calc(_p int) (localctx ICalcContext) {
var _parentctx antlr.ParserRuleContext = p.GetParserRuleContext()
_parentState := p.GetState()
localctx = NewCalcContext(p, p.GetParserRuleContext(), _parentState)
var _prevctx ICalcContext = localctx
var _ antlr.ParserRuleContext = _prevctx // TODO: To prevent unused variable warning.
_startState := 10
p.EnterRecursionRule(localctx, 10, CELParserRULE_calc, _p)
var _la int
var _alt int
p.EnterOuterAlt(localctx, 1)
{
p.SetState(73)
p.Unary()
}
p.GetParserRuleContext().SetStop(p.GetTokenStream().LT(-1))
p.SetState(83)
p.GetErrorHandler().Sync(p)
if p.HasError() {
goto errorExit
}
_alt = p.GetInterpreter().AdaptivePredict(p.BaseParser, p.GetTokenStream(), 5, p.GetParserRuleContext())
if p.HasError() {
goto errorExit
}
for _alt != 2 && _alt != antlr.ATNInvalidAltNumber {
if _alt == 1 {
if p.GetParseListeners() != nil {
p.TriggerExitRuleEvent()
}
_prevctx = localctx
p.SetState(81)
p.GetErrorHandler().Sync(p)
if p.HasError() {
goto errorExit
}
switch p.GetInterpreter().AdaptivePredict(p.BaseParser, p.GetTokenStream(), 4, p.GetParserRuleContext()) {
case 1:
localctx = NewCalcContext(p, _parentctx, _parentState)
p.PushNewRecursionContext(localctx, _startState, CELParserRULE_calc)
p.SetState(75)
if !(p.Precpred(p.GetParserRuleContext(), 2)) {
p.SetError(antlr.NewFailedPredicateException(p, "p.Precpred(p.GetParserRuleContext(), 2)", ""))
goto errorExit
}
{
p.SetState(76)
var _lt = p.GetTokenStream().LT(1)
localctx.(*CalcContext).op = _lt
_la = p.GetTokenStream().LA(1)
if !((int64(_la) & ^0x3f) == 0 && ((int64(1)<<_la)&58720256) != 0) {
var _ri = p.GetErrorHandler().RecoverInline(p)
localctx.(*CalcContext).op = _ri
} else {
p.GetErrorHandler().ReportMatch(p)
p.Consume()
}
}
{
p.SetState(77)
p.calc(3)
}
case 2:
localctx = NewCalcContext(p, _parentctx, _parentState)
p.PushNewRecursionContext(localctx, _startState, CELParserRULE_calc)
p.SetState(78)
if !(p.Precpred(p.GetParserRuleContext(), 1)) {
p.SetError(antlr.NewFailedPredicateException(p, "p.Precpred(p.GetParserRuleContext(), 1)", ""))
goto errorExit
}
{
p.SetState(79)
var _lt = p.GetTokenStream().LT(1)
localctx.(*CalcContext).op = _lt
_la = p.GetTokenStream().LA(1)
if !(_la == CELParserMINUS || _la == CELParserPLUS) {
var _ri = p.GetErrorHandler().RecoverInline(p)
localctx.(*CalcContext).op = _ri
} else {
p.GetErrorHandler().ReportMatch(p)
p.Consume()
}
}
{
p.SetState(80)
p.calc(2)
}
case antlr.ATNInvalidAltNumber:
goto errorExit
}
}
p.SetState(85)
p.GetErrorHandler().Sync(p)
if p.HasError() {
goto errorExit
}
_alt = p.GetInterpreter().AdaptivePredict(p.BaseParser, p.GetTokenStream(), 5, p.GetParserRuleContext())
if p.HasError() {
goto errorExit
}
}
errorExit:
if p.HasError() {
v := p.GetError()
localctx.SetException(v)
p.GetErrorHandler().ReportError(p, v)
p.GetErrorHandler().Recover(p, v)
p.SetError(nil)
}
p.UnrollRecursionContexts(_parentctx)
return localctx
goto errorExit // Trick to prevent compiler error if the label is not used
}
// IUnaryContext is an interface to support dynamic dispatch.
type IUnaryContext interface {
antlr.ParserRuleContext
// GetParser returns the parser.
GetParser() antlr.Parser
// IsUnaryContext differentiates from other interfaces.
IsUnaryContext()
}
type UnaryContext struct {
antlr.BaseParserRuleContext
parser antlr.Parser
}
func NewEmptyUnaryContext() *UnaryContext {
var p = new(UnaryContext)
antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1)
p.RuleIndex = CELParserRULE_unary
return p
}
func InitEmptyUnaryContext(p *UnaryContext) {
antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1)
p.RuleIndex = CELParserRULE_unary
}
func (*UnaryContext) IsUnaryContext() {}
func NewUnaryContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *UnaryContext {
var p = new(UnaryContext)
antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, parent, invokingState)
p.parser = parser
p.RuleIndex = CELParserRULE_unary
return p
}
func (s *UnaryContext) GetParser() antlr.Parser { return s.parser }
func (s *UnaryContext) CopyAll(ctx *UnaryContext) {
s.CopyFrom(&ctx.BaseParserRuleContext)
}
func (s *UnaryContext) GetRuleContext() antlr.RuleContext {
return s
}
func (s *UnaryContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string {
return antlr.TreesStringTree(s, ruleNames, recog)
}
type LogicalNotContext struct {
UnaryContext
s19 antlr.Token
ops []antlr.Token
}
func NewLogicalNotContext(parser antlr.Parser, ctx antlr.ParserRuleContext) *LogicalNotContext {
var p = new(LogicalNotContext)
InitEmptyUnaryContext(&p.UnaryContext)
p.parser = parser
p.CopyAll(ctx.(*UnaryContext))
return p
}
func (s *LogicalNotContext) GetS19() antlr.Token { return s.s19 }
func (s *LogicalNotContext) SetS19(v antlr.Token) { s.s19 = v }
func (s *LogicalNotContext) GetOps() []antlr.Token { return s.ops }
func (s *LogicalNotContext) SetOps(v []antlr.Token) { s.ops = v }
func (s *LogicalNotContext) GetRuleContext() antlr.RuleContext {
return s
}
func (s *LogicalNotContext) Member() IMemberContext {
var t antlr.RuleContext
for _, ctx := range s.GetChildren() {
if _, ok := ctx.(IMemberContext); ok {
t = ctx.(antlr.RuleContext)
break
}
}
if t == nil {
return nil
}
return t.(IMemberContext)
}
func (s *LogicalNotContext) AllEXCLAM() []antlr.TerminalNode {
return s.GetTokens(CELParserEXCLAM)
}
func (s *LogicalNotContext) EXCLAM(i int) antlr.TerminalNode {
return s.GetToken(CELParserEXCLAM, i)
}
func (s *LogicalNotContext) EnterRule(listener antlr.ParseTreeListener) {
if listenerT, ok := listener.(CELListener); ok {
listenerT.EnterLogicalNot(s)
}
}
func (s *LogicalNotContext) ExitRule(listener antlr.ParseTreeListener) {
if listenerT, ok := listener.(CELListener); ok {
listenerT.ExitLogicalNot(s)
}
}
func (s *LogicalNotContext) Accept(visitor antlr.ParseTreeVisitor) interface{} {
switch t := visitor.(type) {
case CELVisitor:
return t.VisitLogicalNot(s)
default:
return t.VisitChildren(s)
}
}
type MemberExprContext struct {
UnaryContext
}
func NewMemberExprContext(parser antlr.Parser, ctx antlr.ParserRuleContext) *MemberExprContext {
var p = new(MemberExprContext)
InitEmptyUnaryContext(&p.UnaryContext)
p.parser = parser
p.CopyAll(ctx.(*UnaryContext))
return p
}
func (s *MemberExprContext) GetRuleContext() antlr.RuleContext {
return s
}
func (s *MemberExprContext) Member() IMemberContext {
var t antlr.RuleContext
for _, ctx := range s.GetChildren() {
if _, ok := ctx.(IMemberContext); ok {
t = ctx.(antlr.RuleContext)
break
}
}
if t == nil {
return nil
}
return t.(IMemberContext)
}
func (s *MemberExprContext) EnterRule(listener antlr.ParseTreeListener) {
if listenerT, ok := listener.(CELListener); ok {
listenerT.EnterMemberExpr(s)
}
}
func (s *MemberExprContext) ExitRule(listener antlr.ParseTreeListener) {
if listenerT, ok := listener.(CELListener); ok {
listenerT.ExitMemberExpr(s)
}
}
func (s *MemberExprContext) Accept(visitor antlr.ParseTreeVisitor) interface{} {
switch t := visitor.(type) {
case CELVisitor:
return t.VisitMemberExpr(s)
default:
return t.VisitChildren(s)
}
}
type NegateContext struct {
UnaryContext
s18 antlr.Token
ops []antlr.Token
}
func NewNegateContext(parser antlr.Parser, ctx antlr.ParserRuleContext) *NegateContext {
var p = new(NegateContext)
InitEmptyUnaryContext(&p.UnaryContext)
p.parser = parser
p.CopyAll(ctx.(*UnaryContext))
return p
}
func (s *NegateContext) GetS18() antlr.Token { return s.s18 }
func (s *NegateContext) SetS18(v antlr.Token) { s.s18 = v }
func (s *NegateContext) GetOps() []antlr.Token { return s.ops }
func (s *NegateContext) SetOps(v []antlr.Token) { s.ops = v }
func (s *NegateContext) GetRuleContext() antlr.RuleContext {
return s
}
func (s *NegateContext) Member() IMemberContext {
var t antlr.RuleContext
for _, ctx := range s.GetChildren() {
if _, ok := ctx.(IMemberContext); ok {
t = ctx.(antlr.RuleContext)
break
}
}
if t == nil {
return nil
}
return t.(IMemberContext)
}
func (s *NegateContext) AllMINUS() []antlr.TerminalNode {
return s.GetTokens(CELParserMINUS)
}
func (s *NegateContext) MINUS(i int) antlr.TerminalNode {
return s.GetToken(CELParserMINUS, i)
}
func (s *NegateContext) EnterRule(listener antlr.ParseTreeListener) {
if listenerT, ok := listener.(CELListener); ok {
listenerT.EnterNegate(s)
}
}
func (s *NegateContext) ExitRule(listener antlr.ParseTreeListener) {
if listenerT, ok := listener.(CELListener); ok {
listenerT.ExitNegate(s)
}
}
func (s *NegateContext) Accept(visitor antlr.ParseTreeVisitor) interface{} {
switch t := visitor.(type) {
case CELVisitor:
return t.VisitNegate(s)
default:
return t.VisitChildren(s)
}
}
func (p *CELParser) Unary() (localctx IUnaryContext) {
localctx = NewUnaryContext(p, p.GetParserRuleContext(), p.GetState())
p.EnterRule(localctx, 12, CELParserRULE_unary)
var _la int
var _alt int
p.SetState(99)
p.GetErrorHandler().Sync(p)
if p.HasError() {
goto errorExit
}
switch p.GetInterpreter().AdaptivePredict(p.BaseParser, p.GetTokenStream(), 8, p.GetParserRuleContext()) {
case 1:
localctx = NewMemberExprContext(p, localctx)
p.EnterOuterAlt(localctx, 1)
{
p.SetState(86)
p.member(0)
}
case 2:
localctx = NewLogicalNotContext(p, localctx)
p.EnterOuterAlt(localctx, 2)
p.SetState(88)
p.GetErrorHandler().Sync(p)
if p.HasError() {
goto errorExit
}
_la = p.GetTokenStream().LA(1)
for ok := true; ok; ok = _la == CELParserEXCLAM {
{
p.SetState(87)
var _m = p.Match(CELParserEXCLAM)
localctx.(*LogicalNotContext).s19 = _m
if p.HasError() {
// Recognition error - abort rule
goto errorExit
}
}
localctx.(*LogicalNotContext).ops = append(localctx.(*LogicalNotContext).ops, localctx.(*LogicalNotContext).s19)
p.SetState(90)
p.GetErrorHandler().Sync(p)
if p.HasError() {
goto errorExit
}
_la = p.GetTokenStream().LA(1)
}
{
p.SetState(92)
p.member(0)
}
case 3:
localctx = NewNegateContext(p, localctx)
p.EnterOuterAlt(localctx, 3)
p.SetState(94)
p.GetErrorHandler().Sync(p)
if p.HasError() {
goto errorExit
}
_alt = 1
for ok := true; ok; ok = _alt != 2 && _alt != antlr.ATNInvalidAltNumber {
switch _alt {
case 1:
{
p.SetState(93)
var _m = p.Match(CELParserMINUS)
localctx.(*NegateContext).s18 = _m
if p.HasError() {
// Recognition error - abort rule
goto errorExit
}
}
localctx.(*NegateContext).ops = append(localctx.(*NegateContext).ops, localctx.(*NegateContext).s18)
default:
p.SetError(antlr.NewNoViableAltException(p, nil, nil, nil, nil, nil))
goto errorExit
}
p.SetState(96)
p.GetErrorHandler().Sync(p)
_alt = p.GetInterpreter().AdaptivePredict(p.BaseParser, p.GetTokenStream(), 7, p.GetParserRuleContext())
if p.HasError() {
goto errorExit
}
}
{
p.SetState(98)
p.member(0)
}
case antlr.ATNInvalidAltNumber:
goto errorExit
}
errorExit:
if p.HasError() {
v := p.GetError()
localctx.SetException(v)
p.GetErrorHandler().ReportError(p, v)
p.GetErrorHandler().Recover(p, v)
p.SetError(nil)
}
p.ExitRule()
return localctx
goto errorExit // Trick to prevent compiler error if the label is not used
}
// IMemberContext is an interface to support dynamic dispatch.
type IMemberContext interface {
antlr.ParserRuleContext
// GetParser returns the parser.
GetParser() antlr.Parser
// IsMemberContext differentiates from other interfaces.
IsMemberContext()
}
type MemberContext struct {
antlr.BaseParserRuleContext
parser antlr.Parser
}
func NewEmptyMemberContext() *MemberContext {
var p = new(MemberContext)
antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1)
p.RuleIndex = CELParserRULE_member
return p
}
func InitEmptyMemberContext(p *MemberContext) {
antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1)
p.RuleIndex = CELParserRULE_member
}
func (*MemberContext) IsMemberContext() {}
func NewMemberContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *MemberContext {
var p = new(MemberContext)
antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, parent, invokingState)
p.parser = parser
p.RuleIndex = CELParserRULE_member
return p
}
func (s *MemberContext) GetParser() antlr.Parser { return s.parser }
func (s *MemberContext) CopyAll(ctx *MemberContext) {
s.CopyFrom(&ctx.BaseParserRuleContext)
}
func (s *MemberContext) GetRuleContext() antlr.RuleContext {
return s
}
func (s *MemberContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string {
return antlr.TreesStringTree(s, ruleNames, recog)
}
type MemberCallContext struct {
MemberContext
op antlr.Token
id antlr.Token
open antlr.Token
args IExprListContext
}
func NewMemberCallContext(parser antlr.Parser, ctx antlr.ParserRuleContext) *MemberCallContext {
var p = new(MemberCallContext)
InitEmptyMemberContext(&p.MemberContext)
p.parser = parser
p.CopyAll(ctx.(*MemberContext))
return p
}
func (s *MemberCallContext) GetOp() antlr.Token { return s.op }
func (s *MemberCallContext) GetId() antlr.Token { return s.id }
func (s *MemberCallContext) GetOpen() antlr.Token { return s.open }
func (s *MemberCallContext) SetOp(v antlr.Token) { s.op = v }
func (s *MemberCallContext) SetId(v antlr.Token) { s.id = v }
func (s *MemberCallContext) SetOpen(v antlr.Token) { s.open = v }
func (s *MemberCallContext) GetArgs() IExprListContext { return s.args }
func (s *MemberCallContext) SetArgs(v IExprListContext) { s.args = v }
func (s *MemberCallContext) GetRuleContext() antlr.RuleContext {
return s
}
func (s *MemberCallContext) Member() IMemberContext {
var t antlr.RuleContext
for _, ctx := range s.GetChildren() {
if _, ok := ctx.(IMemberContext); ok {
t = ctx.(antlr.RuleContext)
break
}
}
if t == nil {
return nil
}
return t.(IMemberContext)
}
func (s *MemberCallContext) RPAREN() antlr.TerminalNode {
return s.GetToken(CELParserRPAREN, 0)
}
func (s *MemberCallContext) DOT() antlr.TerminalNode {
return s.GetToken(CELParserDOT, 0)
}
func (s *MemberCallContext) IDENTIFIER() antlr.TerminalNode {
return s.GetToken(CELParserIDENTIFIER, 0)
}
func (s *MemberCallContext) LPAREN() antlr.TerminalNode {
return s.GetToken(CELParserLPAREN, 0)
}
func (s *MemberCallContext) ExprList() IExprListContext {
var t antlr.RuleContext
for _, ctx := range s.GetChildren() {
if _, ok := ctx.(IExprListContext); ok {
t = ctx.(antlr.RuleContext)
break
}
}
if t == nil {
return nil
}
return t.(IExprListContext)
}
func (s *MemberCallContext) EnterRule(listener antlr.ParseTreeListener) {
if listenerT, ok := listener.(CELListener); ok {
listenerT.EnterMemberCall(s)
}
}
func (s *MemberCallContext) ExitRule(listener antlr.ParseTreeListener) {
if listenerT, ok := listener.(CELListener); ok {
listenerT.ExitMemberCall(s)
}
}
func (s *MemberCallContext) Accept(visitor antlr.ParseTreeVisitor) interface{} {
switch t := visitor.(type) {
case CELVisitor:
return t.VisitMemberCall(s)
default:
return t.VisitChildren(s)
}
}
type SelectContext struct {
MemberContext
op antlr.Token
opt antlr.Token
id IEscapeIdentContext
}
func NewSelectContext(parser antlr.Parser, ctx antlr.ParserRuleContext) *SelectContext {
var p = new(SelectContext)
InitEmptyMemberContext(&p.MemberContext)
p.parser = parser
p.CopyAll(ctx.(*MemberContext))
return p
}
func (s *SelectContext) GetOp() antlr.Token { return s.op }
func (s *SelectContext) GetOpt() antlr.Token { return s.opt }
func (s *SelectContext) SetOp(v antlr.Token) { s.op = v }
func (s *SelectContext) SetOpt(v antlr.Token) { s.opt = v }
func (s *SelectContext) GetId() IEscapeIdentContext { return s.id }
func (s *SelectContext) SetId(v IEscapeIdentContext) { s.id = v }
func (s *SelectContext) GetRuleContext() antlr.RuleContext {
return s
}
func (s *SelectContext) Member() IMemberContext {
var t antlr.RuleContext
for _, ctx := range s.GetChildren() {
if _, ok := ctx.(IMemberContext); ok {
t = ctx.(antlr.RuleContext)
break
}
}
if t == nil {
return nil
}
return t.(IMemberContext)
}
func (s *SelectContext) DOT() antlr.TerminalNode {
return s.GetToken(CELParserDOT, 0)
}
func (s *SelectContext) EscapeIdent() IEscapeIdentContext {
var t antlr.RuleContext
for _, ctx := range s.GetChildren() {
if _, ok := ctx.(IEscapeIdentContext); ok {
t = ctx.(antlr.RuleContext)
break
}
}
if t == nil {
return nil
}
return t.(IEscapeIdentContext)
}
func (s *SelectContext) QUESTIONMARK() antlr.TerminalNode {
return s.GetToken(CELParserQUESTIONMARK, 0)
}
func (s *SelectContext) EnterRule(listener antlr.ParseTreeListener) {
if listenerT, ok := listener.(CELListener); ok {
listenerT.EnterSelect(s)
}
}
func (s *SelectContext) ExitRule(listener antlr.ParseTreeListener) {
if listenerT, ok := listener.(CELListener); ok {
listenerT.ExitSelect(s)
}
}
func (s *SelectContext) Accept(visitor antlr.ParseTreeVisitor) interface{} {
switch t := visitor.(type) {
case CELVisitor:
return t.VisitSelect(s)
default:
return t.VisitChildren(s)
}
}
type PrimaryExprContext struct {
MemberContext
}
func NewPrimaryExprContext(parser antlr.Parser, ctx antlr.ParserRuleContext) *PrimaryExprContext {
var p = new(PrimaryExprContext)
InitEmptyMemberContext(&p.MemberContext)
p.parser = parser
p.CopyAll(ctx.(*MemberContext))
return p
}
func (s *PrimaryExprContext) GetRuleContext() antlr.RuleContext {
return s
}
func (s *PrimaryExprContext) Primary() IPrimaryContext {
var t antlr.RuleContext
for _, ctx := range s.GetChildren() {
if _, ok := ctx.(IPrimaryContext); ok {
t = ctx.(antlr.RuleContext)
break
}
}
if t == nil {
return nil
}
return t.(IPrimaryContext)
}
func (s *PrimaryExprContext) EnterRule(listener antlr.ParseTreeListener) {
if listenerT, ok := listener.(CELListener); ok {
listenerT.EnterPrimaryExpr(s)
}
}
func (s *PrimaryExprContext) ExitRule(listener antlr.ParseTreeListener) {
if listenerT, ok := listener.(CELListener); ok {
listenerT.ExitPrimaryExpr(s)
}
}
func (s *PrimaryExprContext) Accept(visitor antlr.ParseTreeVisitor) interface{} {
switch t := visitor.(type) {
case CELVisitor:
return t.VisitPrimaryExpr(s)
default:
return t.VisitChildren(s)
}
}
type IndexContext struct {
MemberContext
op antlr.Token
opt antlr.Token
index IExprContext
}
func NewIndexContext(parser antlr.Parser, ctx antlr.ParserRuleContext) *IndexContext {
var p = new(IndexContext)
InitEmptyMemberContext(&p.MemberContext)
p.parser = parser
p.CopyAll(ctx.(*MemberContext))
return p
}
func (s *IndexContext) GetOp() antlr.Token { return s.op }
func (s *IndexContext) GetOpt() antlr.Token { return s.opt }
func (s *IndexContext) SetOp(v antlr.Token) { s.op = v }
func (s *IndexContext) SetOpt(v antlr.Token) { s.opt = v }
func (s *IndexContext) GetIndex() IExprContext { return s.index }
func (s *IndexContext) SetIndex(v IExprContext) { s.index = v }
func (s *IndexContext) GetRuleContext() antlr.RuleContext {
return s
}
func (s *IndexContext) Member() IMemberContext {
var t antlr.RuleContext
for _, ctx := range s.GetChildren() {
if _, ok := ctx.(IMemberContext); ok {
t = ctx.(antlr.RuleContext)
break
}
}
if t == nil {
return nil
}
return t.(IMemberContext)
}
func (s *IndexContext) RPRACKET() antlr.TerminalNode {
return s.GetToken(CELParserRPRACKET, 0)
}
func (s *IndexContext) LBRACKET() antlr.TerminalNode {
return s.GetToken(CELParserLBRACKET, 0)
}
func (s *IndexContext) Expr() IExprContext {
var t antlr.RuleContext
for _, ctx := range s.GetChildren() {
if _, ok := ctx.(IExprContext); ok {
t = ctx.(antlr.RuleContext)
break
}
}
if t == nil {
return nil
}
return t.(IExprContext)
}
func (s *IndexContext) QUESTIONMARK() antlr.TerminalNode {
return s.GetToken(CELParserQUESTIONMARK, 0)
}
func (s *IndexContext) EnterRule(listener antlr.ParseTreeListener) {
if listenerT, ok := listener.(CELListener); ok {
listenerT.EnterIndex(s)
}
}
func (s *IndexContext) ExitRule(listener antlr.ParseTreeListener) {
if listenerT, ok := listener.(CELListener); ok {
listenerT.ExitIndex(s)
}
}
func (s *IndexContext) Accept(visitor antlr.ParseTreeVisitor) interface{} {
switch t := visitor.(type) {
case CELVisitor:
return t.VisitIndex(s)
default:
return t.VisitChildren(s)
}
}
func (p *CELParser) Member() (localctx IMemberContext) {
return p.member(0)
}
func (p *CELParser) member(_p int) (localctx IMemberContext) {
var _parentctx antlr.ParserRuleContext = p.GetParserRuleContext()
_parentState := p.GetState()
localctx = NewMemberContext(p, p.GetParserRuleContext(), _parentState)
var _prevctx IMemberContext = localctx
var _ antlr.ParserRuleContext = _prevctx // TODO: To prevent unused variable warning.
_startState := 14
p.EnterRecursionRule(localctx, 14, CELParserRULE_member, _p)
var _la int
var _alt int
p.EnterOuterAlt(localctx, 1)
localctx = NewPrimaryExprContext(p, localctx)
p.SetParserRuleContext(localctx)
_prevctx = localctx
{
p.SetState(102)
p.Primary()
}
p.GetParserRuleContext().SetStop(p.GetTokenStream().LT(-1))
p.SetState(128)
p.GetErrorHandler().Sync(p)
if p.HasError() {
goto errorExit
}
_alt = p.GetInterpreter().AdaptivePredict(p.BaseParser, p.GetTokenStream(), 13, p.GetParserRuleContext())
if p.HasError() {
goto errorExit
}
for _alt != 2 && _alt != antlr.ATNInvalidAltNumber {
if _alt == 1 {
if p.GetParseListeners() != nil {
p.TriggerExitRuleEvent()
}
_prevctx = localctx
p.SetState(126)
p.GetErrorHandler().Sync(p)
if p.HasError() {
goto errorExit
}
switch p.GetInterpreter().AdaptivePredict(p.BaseParser, p.GetTokenStream(), 12, p.GetParserRuleContext()) {
case 1:
localctx = NewSelectContext(p, NewMemberContext(p, _parentctx, _parentState))
p.PushNewRecursionContext(localctx, _startState, CELParserRULE_member)
p.SetState(104)
if !(p.Precpred(p.GetParserRuleContext(), 3)) {
p.SetError(antlr.NewFailedPredicateException(p, "p.Precpred(p.GetParserRuleContext(), 3)", ""))
goto errorExit
}
{
p.SetState(105)
var _m = p.Match(CELParserDOT)
localctx.(*SelectContext).op = _m
if p.HasError() {
// Recognition error - abort rule
goto errorExit
}
}
p.SetState(107)
p.GetErrorHandler().Sync(p)
if p.HasError() {
goto errorExit
}
_la = p.GetTokenStream().LA(1)
if _la == CELParserQUESTIONMARK {
{
p.SetState(106)
var _m = p.Match(CELParserQUESTIONMARK)
localctx.(*SelectContext).opt = _m
if p.HasError() {
// Recognition error - abort rule
goto errorExit
}
}
}
{
p.SetState(109)
var _x = p.EscapeIdent()
localctx.(*SelectContext).id = _x
}
case 2:
localctx = NewMemberCallContext(p, NewMemberContext(p, _parentctx, _parentState))
p.PushNewRecursionContext(localctx, _startState, CELParserRULE_member)
p.SetState(110)
if !(p.Precpred(p.GetParserRuleContext(), 2)) {
p.SetError(antlr.NewFailedPredicateException(p, "p.Precpred(p.GetParserRuleContext(), 2)", ""))
goto errorExit
}
{
p.SetState(111)
var _m = p.Match(CELParserDOT)
localctx.(*MemberCallContext).op = _m
if p.HasError() {
// Recognition error - abort rule
goto errorExit
}
}
{
p.SetState(112)
var _m = p.Match(CELParserIDENTIFIER)
localctx.(*MemberCallContext).id = _m
if p.HasError() {
// Recognition error - abort rule
goto errorExit
}
}
{
p.SetState(113)
var _m = p.Match(CELParserLPAREN)
localctx.(*MemberCallContext).open = _m
if p.HasError() {
// Recognition error - abort rule
goto errorExit
}
}
p.SetState(115)
p.GetErrorHandler().Sync(p)
if p.HasError() {
goto errorExit
}
_la = p.GetTokenStream().LA(1)
if (int64(_la) & ^0x3f) == 0 && ((int64(1)<<_la)&135762105344) != 0 {
{
p.SetState(114)
var _x = p.ExprList()
localctx.(*MemberCallContext).args = _x
}
}
{
p.SetState(117)
p.Match(CELParserRPAREN)
if p.HasError() {
// Recognition error - abort rule
goto errorExit
}
}
case 3:
localctx = NewIndexContext(p, NewMemberContext(p, _parentctx, _parentState))
p.PushNewRecursionContext(localctx, _startState, CELParserRULE_member)
p.SetState(118)
if !(p.Precpred(p.GetParserRuleContext(), 1)) {
p.SetError(antlr.NewFailedPredicateException(p, "p.Precpred(p.GetParserRuleContext(), 1)", ""))
goto errorExit
}
{
p.SetState(119)
var _m = p.Match(CELParserLBRACKET)
localctx.(*IndexContext).op = _m
if p.HasError() {
// Recognition error - abort rule
goto errorExit
}
}
p.SetState(121)
p.GetErrorHandler().Sync(p)
if p.HasError() {
goto errorExit
}
_la = p.GetTokenStream().LA(1)
if _la == CELParserQUESTIONMARK {
{
p.SetState(120)
var _m = p.Match(CELParserQUESTIONMARK)
localctx.(*IndexContext).opt = _m
if p.HasError() {
// Recognition error - abort rule
goto errorExit
}
}
}
{
p.SetState(123)
var _x = p.Expr()
localctx.(*IndexContext).index = _x
}
{
p.SetState(124)
p.Match(CELParserRPRACKET)
if p.HasError() {
// Recognition error - abort rule
goto errorExit
}
}
case antlr.ATNInvalidAltNumber:
goto errorExit
}
}
p.SetState(130)
p.GetErrorHandler().Sync(p)
if p.HasError() {
goto errorExit
}
_alt = p.GetInterpreter().AdaptivePredict(p.BaseParser, p.GetTokenStream(), 13, p.GetParserRuleContext())
if p.HasError() {
goto errorExit
}
}
errorExit:
if p.HasError() {
v := p.GetError()
localctx.SetException(v)
p.GetErrorHandler().ReportError(p, v)
p.GetErrorHandler().Recover(p, v)
p.SetError(nil)
}
p.UnrollRecursionContexts(_parentctx)
return localctx
goto errorExit // Trick to prevent compiler error if the label is not used
}
// IPrimaryContext is an interface to support dynamic dispatch.
type IPrimaryContext interface {
antlr.ParserRuleContext
// GetParser returns the parser.
GetParser() antlr.Parser
// IsPrimaryContext differentiates from other interfaces.
IsPrimaryContext()
}
type PrimaryContext struct {
antlr.BaseParserRuleContext
parser antlr.Parser
}
func NewEmptyPrimaryContext() *PrimaryContext {
var p = new(PrimaryContext)
antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1)
p.RuleIndex = CELParserRULE_primary
return p
}
func InitEmptyPrimaryContext(p *PrimaryContext) {
antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1)
p.RuleIndex = CELParserRULE_primary
}
func (*PrimaryContext) IsPrimaryContext() {}
func NewPrimaryContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *PrimaryContext {
var p = new(PrimaryContext)
antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, parent, invokingState)
p.parser = parser
p.RuleIndex = CELParserRULE_primary
return p
}
func (s *PrimaryContext) GetParser() antlr.Parser { return s.parser }
func (s *PrimaryContext) CopyAll(ctx *PrimaryContext) {
s.CopyFrom(&ctx.BaseParserRuleContext)
}
func (s *PrimaryContext) GetRuleContext() antlr.RuleContext {
return s
}
func (s *PrimaryContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string {
return antlr.TreesStringTree(s, ruleNames, recog)
}
type CreateListContext struct {
PrimaryContext
op antlr.Token
elems IListInitContext
}
func NewCreateListContext(parser antlr.Parser, ctx antlr.ParserRuleContext) *CreateListContext {
var p = new(CreateListContext)
InitEmptyPrimaryContext(&p.PrimaryContext)
p.parser = parser
p.CopyAll(ctx.(*PrimaryContext))
return p
}
func (s *CreateListContext) GetOp() antlr.Token { return s.op }
func (s *CreateListContext) SetOp(v antlr.Token) { s.op = v }
func (s *CreateListContext) GetElems() IListInitContext { return s.elems }
func (s *CreateListContext) SetElems(v IListInitContext) { s.elems = v }
func (s *CreateListContext) GetRuleContext() antlr.RuleContext {
return s
}
func (s *CreateListContext) RPRACKET() antlr.TerminalNode {
return s.GetToken(CELParserRPRACKET, 0)
}
func (s *CreateListContext) LBRACKET() antlr.TerminalNode {
return s.GetToken(CELParserLBRACKET, 0)
}
func (s *CreateListContext) COMMA() antlr.TerminalNode {
return s.GetToken(CELParserCOMMA, 0)
}
func (s *CreateListContext) ListInit() IListInitContext {
var t antlr.RuleContext
for _, ctx := range s.GetChildren() {
if _, ok := ctx.(IListInitContext); ok {
t = ctx.(antlr.RuleContext)
break
}
}
if t == nil {
return nil
}
return t.(IListInitContext)
}
func (s *CreateListContext) EnterRule(listener antlr.ParseTreeListener) {
if listenerT, ok := listener.(CELListener); ok {
listenerT.EnterCreateList(s)
}
}
func (s *CreateListContext) ExitRule(listener antlr.ParseTreeListener) {
if listenerT, ok := listener.(CELListener); ok {
listenerT.ExitCreateList(s)
}
}
func (s *CreateListContext) Accept(visitor antlr.ParseTreeVisitor) interface{} {
switch t := visitor.(type) {
case CELVisitor:
return t.VisitCreateList(s)
default:
return t.VisitChildren(s)
}
}
type IdentContext struct {
PrimaryContext
leadingDot antlr.Token
id antlr.Token
}
func NewIdentContext(parser antlr.Parser, ctx antlr.ParserRuleContext) *IdentContext {
var p = new(IdentContext)
InitEmptyPrimaryContext(&p.PrimaryContext)
p.parser = parser
p.CopyAll(ctx.(*PrimaryContext))
return p
}
func (s *IdentContext) GetLeadingDot() antlr.Token { return s.leadingDot }
func (s *IdentContext) GetId() antlr.Token { return s.id }
func (s *IdentContext) SetLeadingDot(v antlr.Token) { s.leadingDot = v }
func (s *IdentContext) SetId(v antlr.Token) { s.id = v }
func (s *IdentContext) GetRuleContext() antlr.RuleContext {
return s
}
func (s *IdentContext) IDENTIFIER() antlr.TerminalNode {
return s.GetToken(CELParserIDENTIFIER, 0)
}
func (s *IdentContext) DOT() antlr.TerminalNode {
return s.GetToken(CELParserDOT, 0)
}
func (s *IdentContext) EnterRule(listener antlr.ParseTreeListener) {
if listenerT, ok := listener.(CELListener); ok {
listenerT.EnterIdent(s)
}
}
func (s *IdentContext) ExitRule(listener antlr.ParseTreeListener) {
if listenerT, ok := listener.(CELListener); ok {
listenerT.ExitIdent(s)
}
}
func (s *IdentContext) Accept(visitor antlr.ParseTreeVisitor) interface{} {
switch t := visitor.(type) {
case CELVisitor:
return t.VisitIdent(s)
default:
return t.VisitChildren(s)
}
}
type CreateStructContext struct {
PrimaryContext
op antlr.Token
entries IMapInitializerListContext
}
func NewCreateStructContext(parser antlr.Parser, ctx antlr.ParserRuleContext) *CreateStructContext {
var p = new(CreateStructContext)
InitEmptyPrimaryContext(&p.PrimaryContext)
p.parser = parser
p.CopyAll(ctx.(*PrimaryContext))
return p
}
func (s *CreateStructContext) GetOp() antlr.Token { return s.op }
func (s *CreateStructContext) SetOp(v antlr.Token) { s.op = v }
func (s *CreateStructContext) GetEntries() IMapInitializerListContext { return s.entries }
func (s *CreateStructContext) SetEntries(v IMapInitializerListContext) { s.entries = v }
func (s *CreateStructContext) GetRuleContext() antlr.RuleContext {
return s
}
func (s *CreateStructContext) RBRACE() antlr.TerminalNode {
return s.GetToken(CELParserRBRACE, 0)
}
func (s *CreateStructContext) LBRACE() antlr.TerminalNode {
return s.GetToken(CELParserLBRACE, 0)
}
func (s *CreateStructContext) COMMA() antlr.TerminalNode {
return s.GetToken(CELParserCOMMA, 0)
}
func (s *CreateStructContext) MapInitializerList() IMapInitializerListContext {
var t antlr.RuleContext
for _, ctx := range s.GetChildren() {
if _, ok := ctx.(IMapInitializerListContext); ok {
t = ctx.(antlr.RuleContext)
break
}
}
if t == nil {
return nil
}
return t.(IMapInitializerListContext)
}
func (s *CreateStructContext) EnterRule(listener antlr.ParseTreeListener) {
if listenerT, ok := listener.(CELListener); ok {
listenerT.EnterCreateStruct(s)
}
}
func (s *CreateStructContext) ExitRule(listener antlr.ParseTreeListener) {
if listenerT, ok := listener.(CELListener); ok {
listenerT.ExitCreateStruct(s)
}
}
func (s *CreateStructContext) Accept(visitor antlr.ParseTreeVisitor) interface{} {
switch t := visitor.(type) {
case CELVisitor:
return t.VisitCreateStruct(s)
default:
return t.VisitChildren(s)
}
}
type ConstantLiteralContext struct {
PrimaryContext
}
func NewConstantLiteralContext(parser antlr.Parser, ctx antlr.ParserRuleContext) *ConstantLiteralContext {
var p = new(ConstantLiteralContext)
InitEmptyPrimaryContext(&p.PrimaryContext)
p.parser = parser
p.CopyAll(ctx.(*PrimaryContext))
return p
}
func (s *ConstantLiteralContext) GetRuleContext() antlr.RuleContext {
return s
}
func (s *ConstantLiteralContext) Literal() ILiteralContext {
var t antlr.RuleContext
for _, ctx := range s.GetChildren() {
if _, ok := ctx.(ILiteralContext); ok {
t = ctx.(antlr.RuleContext)
break
}
}
if t == nil {
return nil
}
return t.(ILiteralContext)
}
func (s *ConstantLiteralContext) EnterRule(listener antlr.ParseTreeListener) {
if listenerT, ok := listener.(CELListener); ok {
listenerT.EnterConstantLiteral(s)
}
}
func (s *ConstantLiteralContext) ExitRule(listener antlr.ParseTreeListener) {
if listenerT, ok := listener.(CELListener); ok {
listenerT.ExitConstantLiteral(s)
}
}
func (s *ConstantLiteralContext) Accept(visitor antlr.ParseTreeVisitor) interface{} {
switch t := visitor.(type) {
case CELVisitor:
return t.VisitConstantLiteral(s)
default:
return t.VisitChildren(s)
}
}
type NestedContext struct {
PrimaryContext
e IExprContext
}
func NewNestedContext(parser antlr.Parser, ctx antlr.ParserRuleContext) *NestedContext {
var p = new(NestedContext)
InitEmptyPrimaryContext(&p.PrimaryContext)
p.parser = parser
p.CopyAll(ctx.(*PrimaryContext))
return p
}
func (s *NestedContext) GetE() IExprContext { return s.e }
func (s *NestedContext) SetE(v IExprContext) { s.e = v }
func (s *NestedContext) GetRuleContext() antlr.RuleContext {
return s
}
func (s *NestedContext) LPAREN() antlr.TerminalNode {
return s.GetToken(CELParserLPAREN, 0)
}
func (s *NestedContext) RPAREN() antlr.TerminalNode {
return s.GetToken(CELParserRPAREN, 0)
}
func (s *NestedContext) Expr() IExprContext {
var t antlr.RuleContext
for _, ctx := range s.GetChildren() {
if _, ok := ctx.(IExprContext); ok {
t = ctx.(antlr.RuleContext)
break
}
}
if t == nil {
return nil
}
return t.(IExprContext)
}
func (s *NestedContext) EnterRule(listener antlr.ParseTreeListener) {
if listenerT, ok := listener.(CELListener); ok {
listenerT.EnterNested(s)
}
}
func (s *NestedContext) ExitRule(listener antlr.ParseTreeListener) {
if listenerT, ok := listener.(CELListener); ok {
listenerT.ExitNested(s)
}
}
func (s *NestedContext) Accept(visitor antlr.ParseTreeVisitor) interface{} {
switch t := visitor.(type) {
case CELVisitor:
return t.VisitNested(s)
default:
return t.VisitChildren(s)
}
}
type CreateMessageContext struct {
PrimaryContext
leadingDot antlr.Token
_IDENTIFIER antlr.Token
ids []antlr.Token
s16 antlr.Token
ops []antlr.Token
op antlr.Token
entries IFieldInitializerListContext
}
func NewCreateMessageContext(parser antlr.Parser, ctx antlr.ParserRuleContext) *CreateMessageContext {
var p = new(CreateMessageContext)
InitEmptyPrimaryContext(&p.PrimaryContext)
p.parser = parser
p.CopyAll(ctx.(*PrimaryContext))
return p
}
func (s *CreateMessageContext) GetLeadingDot() antlr.Token { return s.leadingDot }
func (s *CreateMessageContext) Get_IDENTIFIER() antlr.Token { return s._IDENTIFIER }
func (s *CreateMessageContext) GetS16() antlr.Token { return s.s16 }
func (s *CreateMessageContext) GetOp() antlr.Token { return s.op }
func (s *CreateMessageContext) SetLeadingDot(v antlr.Token) { s.leadingDot = v }
func (s *CreateMessageContext) Set_IDENTIFIER(v antlr.Token) { s._IDENTIFIER = v }
func (s *CreateMessageContext) SetS16(v antlr.Token) { s.s16 = v }
func (s *CreateMessageContext) SetOp(v antlr.Token) { s.op = v }
func (s *CreateMessageContext) GetIds() []antlr.Token { return s.ids }
func (s *CreateMessageContext) GetOps() []antlr.Token { return s.ops }
func (s *CreateMessageContext) SetIds(v []antlr.Token) { s.ids = v }
func (s *CreateMessageContext) SetOps(v []antlr.Token) { s.ops = v }
func (s *CreateMessageContext) GetEntries() IFieldInitializerListContext { return s.entries }
func (s *CreateMessageContext) SetEntries(v IFieldInitializerListContext) { s.entries = v }
func (s *CreateMessageContext) GetRuleContext() antlr.RuleContext {
return s
}
func (s *CreateMessageContext) RBRACE() antlr.TerminalNode {
return s.GetToken(CELParserRBRACE, 0)
}
func (s *CreateMessageContext) AllIDENTIFIER() []antlr.TerminalNode {
return s.GetTokens(CELParserIDENTIFIER)
}
func (s *CreateMessageContext) IDENTIFIER(i int) antlr.TerminalNode {
return s.GetToken(CELParserIDENTIFIER, i)
}
func (s *CreateMessageContext) LBRACE() antlr.TerminalNode {
return s.GetToken(CELParserLBRACE, 0)
}
func (s *CreateMessageContext) COMMA() antlr.TerminalNode {
return s.GetToken(CELParserCOMMA, 0)
}
func (s *CreateMessageContext) AllDOT() []antlr.TerminalNode {
return s.GetTokens(CELParserDOT)
}
func (s *CreateMessageContext) DOT(i int) antlr.TerminalNode {
return s.GetToken(CELParserDOT, i)
}
func (s *CreateMessageContext) FieldInitializerList() IFieldInitializerListContext {
var t antlr.RuleContext
for _, ctx := range s.GetChildren() {
if _, ok := ctx.(IFieldInitializerListContext); ok {
t = ctx.(antlr.RuleContext)
break
}
}
if t == nil {
return nil
}
return t.(IFieldInitializerListContext)
}
func (s *CreateMessageContext) EnterRule(listener antlr.ParseTreeListener) {
if listenerT, ok := listener.(CELListener); ok {
listenerT.EnterCreateMessage(s)
}
}
func (s *CreateMessageContext) ExitRule(listener antlr.ParseTreeListener) {
if listenerT, ok := listener.(CELListener); ok {
listenerT.ExitCreateMessage(s)
}
}
func (s *CreateMessageContext) Accept(visitor antlr.ParseTreeVisitor) interface{} {
switch t := visitor.(type) {
case CELVisitor:
return t.VisitCreateMessage(s)
default:
return t.VisitChildren(s)
}
}
type GlobalCallContext struct {
PrimaryContext
leadingDot antlr.Token
id antlr.Token
op antlr.Token
args IExprListContext
}
func NewGlobalCallContext(parser antlr.Parser, ctx antlr.ParserRuleContext) *GlobalCallContext {
var p = new(GlobalCallContext)
InitEmptyPrimaryContext(&p.PrimaryContext)
p.parser = parser
p.CopyAll(ctx.(*PrimaryContext))
return p
}
func (s *GlobalCallContext) GetLeadingDot() antlr.Token { return s.leadingDot }
func (s *GlobalCallContext) GetId() antlr.Token { return s.id }
func (s *GlobalCallContext) GetOp() antlr.Token { return s.op }
func (s *GlobalCallContext) SetLeadingDot(v antlr.Token) { s.leadingDot = v }
func (s *GlobalCallContext) SetId(v antlr.Token) { s.id = v }
func (s *GlobalCallContext) SetOp(v antlr.Token) { s.op = v }
func (s *GlobalCallContext) GetArgs() IExprListContext { return s.args }
func (s *GlobalCallContext) SetArgs(v IExprListContext) { s.args = v }
func (s *GlobalCallContext) GetRuleContext() antlr.RuleContext {
return s
}
func (s *GlobalCallContext) IDENTIFIER() antlr.TerminalNode {
return s.GetToken(CELParserIDENTIFIER, 0)
}
func (s *GlobalCallContext) RPAREN() antlr.TerminalNode {
return s.GetToken(CELParserRPAREN, 0)
}
func (s *GlobalCallContext) LPAREN() antlr.TerminalNode {
return s.GetToken(CELParserLPAREN, 0)
}
func (s *GlobalCallContext) DOT() antlr.TerminalNode {
return s.GetToken(CELParserDOT, 0)
}
func (s *GlobalCallContext) ExprList() IExprListContext {
var t antlr.RuleContext
for _, ctx := range s.GetChildren() {
if _, ok := ctx.(IExprListContext); ok {
t = ctx.(antlr.RuleContext)
break
}
}
if t == nil {
return nil
}
return t.(IExprListContext)
}
func (s *GlobalCallContext) EnterRule(listener antlr.ParseTreeListener) {
if listenerT, ok := listener.(CELListener); ok {
listenerT.EnterGlobalCall(s)
}
}
func (s *GlobalCallContext) ExitRule(listener antlr.ParseTreeListener) {
if listenerT, ok := listener.(CELListener); ok {
listenerT.ExitGlobalCall(s)
}
}
func (s *GlobalCallContext) Accept(visitor antlr.ParseTreeVisitor) interface{} {
switch t := visitor.(type) {
case CELVisitor:
return t.VisitGlobalCall(s)
default:
return t.VisitChildren(s)
}
}
func (p *CELParser) Primary() (localctx IPrimaryContext) {
localctx = NewPrimaryContext(p, p.GetParserRuleContext(), p.GetState())
p.EnterRule(localctx, 16, CELParserRULE_primary)
var _la int
p.SetState(184)
p.GetErrorHandler().Sync(p)
if p.HasError() {
goto errorExit
}
switch p.GetInterpreter().AdaptivePredict(p.BaseParser, p.GetTokenStream(), 25, p.GetParserRuleContext()) {
case 1:
localctx = NewIdentContext(p, localctx)
p.EnterOuterAlt(localctx, 1)
p.SetState(132)
p.GetErrorHandler().Sync(p)
if p.HasError() {
goto errorExit
}
_la = p.GetTokenStream().LA(1)
if _la == CELParserDOT {
{
p.SetState(131)
var _m = p.Match(CELParserDOT)
localctx.(*IdentContext).leadingDot = _m
if p.HasError() {
// Recognition error - abort rule
goto errorExit
}
}
}
{
p.SetState(134)
var _m = p.Match(CELParserIDENTIFIER)
localctx.(*IdentContext).id = _m
if p.HasError() {
// Recognition error - abort rule
goto errorExit
}
}
case 2:
localctx = NewGlobalCallContext(p, localctx)
p.EnterOuterAlt(localctx, 2)
p.SetState(136)
p.GetErrorHandler().Sync(p)
if p.HasError() {
goto errorExit
}
_la = p.GetTokenStream().LA(1)
if _la == CELParserDOT {
{
p.SetState(135)
var _m = p.Match(CELParserDOT)
localctx.(*GlobalCallContext).leadingDot = _m
if p.HasError() {
// Recognition error - abort rule
goto errorExit
}
}
}
{
p.SetState(138)
var _m = p.Match(CELParserIDENTIFIER)
localctx.(*GlobalCallContext).id = _m
if p.HasError() {
// Recognition error - abort rule
goto errorExit
}
}
{
p.SetState(139)
var _m = p.Match(CELParserLPAREN)
localctx.(*GlobalCallContext).op = _m
if p.HasError() {
// Recognition error - abort rule
goto errorExit
}
}
p.SetState(141)
p.GetErrorHandler().Sync(p)
if p.HasError() {
goto errorExit
}
_la = p.GetTokenStream().LA(1)
if (int64(_la) & ^0x3f) == 0 && ((int64(1)<<_la)&135762105344) != 0 {
{
p.SetState(140)
var _x = p.ExprList()
localctx.(*GlobalCallContext).args = _x
}
}
{
p.SetState(143)
p.Match(CELParserRPAREN)
if p.HasError() {
// Recognition error - abort rule
goto errorExit
}
}
case 3:
localctx = NewNestedContext(p, localctx)
p.EnterOuterAlt(localctx, 3)
{
p.SetState(144)
p.Match(CELParserLPAREN)
if p.HasError() {
// Recognition error - abort rule
goto errorExit
}
}
{
p.SetState(145)
var _x = p.Expr()
localctx.(*NestedContext).e = _x
}
{
p.SetState(146)
p.Match(CELParserRPAREN)
if p.HasError() {
// Recognition error - abort rule
goto errorExit
}
}
case 4:
localctx = NewCreateListContext(p, localctx)
p.EnterOuterAlt(localctx, 4)
{
p.SetState(148)
var _m = p.Match(CELParserLBRACKET)
localctx.(*CreateListContext).op = _m
if p.HasError() {
// Recognition error - abort rule
goto errorExit
}
}
p.SetState(150)
p.GetErrorHandler().Sync(p)
if p.HasError() {
goto errorExit
}
_la = p.GetTokenStream().LA(1)
if (int64(_la) & ^0x3f) == 0 && ((int64(1)<<_la)&135763153920) != 0 {
{
p.SetState(149)
var _x = p.ListInit()
localctx.(*CreateListContext).elems = _x
}
}
p.SetState(153)
p.GetErrorHandler().Sync(p)
if p.HasError() {
goto errorExit
}
_la = p.GetTokenStream().LA(1)
if _la == CELParserCOMMA {
{
p.SetState(152)
p.Match(CELParserCOMMA)
if p.HasError() {
// Recognition error - abort rule
goto errorExit
}
}
}
{
p.SetState(155)
p.Match(CELParserRPRACKET)
if p.HasError() {
// Recognition error - abort rule
goto errorExit
}
}
case 5:
localctx = NewCreateStructContext(p, localctx)
p.EnterOuterAlt(localctx, 5)
{
p.SetState(156)
var _m = p.Match(CELParserLBRACE)
localctx.(*CreateStructContext).op = _m
if p.HasError() {
// Recognition error - abort rule
goto errorExit
}
}
p.SetState(158)
p.GetErrorHandler().Sync(p)
if p.HasError() {
goto errorExit
}
_la = p.GetTokenStream().LA(1)
if (int64(_la) & ^0x3f) == 0 && ((int64(1)<<_la)&135763153920) != 0 {
{
p.SetState(157)
var _x = p.MapInitializerList()
localctx.(*CreateStructContext).entries = _x
}
}
p.SetState(161)
p.GetErrorHandler().Sync(p)
if p.HasError() {
goto errorExit
}
_la = p.GetTokenStream().LA(1)
if _la == CELParserCOMMA {
{
p.SetState(160)
p.Match(CELParserCOMMA)
if p.HasError() {
// Recognition error - abort rule
goto errorExit
}
}
}
{
p.SetState(163)
p.Match(CELParserRBRACE)
if p.HasError() {
// Recognition error - abort rule
goto errorExit
}
}
case 6:
localctx = NewCreateMessageContext(p, localctx)
p.EnterOuterAlt(localctx, 6)
p.SetState(165)
p.GetErrorHandler().Sync(p)
if p.HasError() {
goto errorExit
}
_la = p.GetTokenStream().LA(1)
if _la == CELParserDOT {
{
p.SetState(164)
var _m = p.Match(CELParserDOT)
localctx.(*CreateMessageContext).leadingDot = _m
if p.HasError() {
// Recognition error - abort rule
goto errorExit
}
}
}
{
p.SetState(167)
var _m = p.Match(CELParserIDENTIFIER)
localctx.(*CreateMessageContext)._IDENTIFIER = _m
if p.HasError() {
// Recognition error - abort rule
goto errorExit
}
}
localctx.(*CreateMessageContext).ids = append(localctx.(*CreateMessageContext).ids, localctx.(*CreateMessageContext)._IDENTIFIER)
p.SetState(172)
p.GetErrorHandler().Sync(p)
if p.HasError() {
goto errorExit
}
_la = p.GetTokenStream().LA(1)
for _la == CELParserDOT {
{
p.SetState(168)
var _m = p.Match(CELParserDOT)
localctx.(*CreateMessageContext).s16 = _m
if p.HasError() {
// Recognition error - abort rule
goto errorExit
}
}
localctx.(*CreateMessageContext).ops = append(localctx.(*CreateMessageContext).ops, localctx.(*CreateMessageContext).s16)
{
p.SetState(169)
var _m = p.Match(CELParserIDENTIFIER)
localctx.(*CreateMessageContext)._IDENTIFIER = _m
if p.HasError() {
// Recognition error - abort rule
goto errorExit
}
}
localctx.(*CreateMessageContext).ids = append(localctx.(*CreateMessageContext).ids, localctx.(*CreateMessageContext)._IDENTIFIER)
p.SetState(174)
p.GetErrorHandler().Sync(p)
if p.HasError() {
goto errorExit
}
_la = p.GetTokenStream().LA(1)
}
{
p.SetState(175)
var _m = p.Match(CELParserLBRACE)
localctx.(*CreateMessageContext).op = _m
if p.HasError() {
// Recognition error - abort rule
goto errorExit
}
}
p.SetState(177)
p.GetErrorHandler().Sync(p)
if p.HasError() {
goto errorExit
}
_la = p.GetTokenStream().LA(1)
if (int64(_la) & ^0x3f) == 0 && ((int64(1)<<_la)&206159478784) != 0 {
{
p.SetState(176)
var _x = p.FieldInitializerList()
localctx.(*CreateMessageContext).entries = _x
}
}
p.SetState(180)
p.GetErrorHandler().Sync(p)
if p.HasError() {
goto errorExit
}
_la = p.GetTokenStream().LA(1)
if _la == CELParserCOMMA {
{
p.SetState(179)
p.Match(CELParserCOMMA)
if p.HasError() {
// Recognition error - abort rule
goto errorExit
}
}
}
{
p.SetState(182)
p.Match(CELParserRBRACE)
if p.HasError() {
// Recognition error - abort rule
goto errorExit
}
}
case 7:
localctx = NewConstantLiteralContext(p, localctx)
p.EnterOuterAlt(localctx, 7)
{
p.SetState(183)
p.Literal()
}
case antlr.ATNInvalidAltNumber:
goto errorExit
}
errorExit:
if p.HasError() {
v := p.GetError()
localctx.SetException(v)
p.GetErrorHandler().ReportError(p, v)
p.GetErrorHandler().Recover(p, v)
p.SetError(nil)
}
p.ExitRule()
return localctx
goto errorExit // Trick to prevent compiler error if the label is not used
}
// IExprListContext is an interface to support dynamic dispatch.
type IExprListContext interface {
antlr.ParserRuleContext
// GetParser returns the parser.
GetParser() antlr.Parser
// Get_expr returns the _expr rule contexts.
Get_expr() IExprContext
// Set_expr sets the _expr rule contexts.
Set_expr(IExprContext)
// GetE returns the e rule context list.
GetE() []IExprContext
// SetE sets the e rule context list.
SetE([]IExprContext)
// Getter signatures
AllExpr() []IExprContext
Expr(i int) IExprContext
AllCOMMA() []antlr.TerminalNode
COMMA(i int) antlr.TerminalNode
// IsExprListContext differentiates from other interfaces.
IsExprListContext()
}
type ExprListContext struct {
antlr.BaseParserRuleContext
parser antlr.Parser
_expr IExprContext
e []IExprContext
}
func NewEmptyExprListContext() *ExprListContext {
var p = new(ExprListContext)
antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1)
p.RuleIndex = CELParserRULE_exprList
return p
}
func InitEmptyExprListContext(p *ExprListContext) {
antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1)
p.RuleIndex = CELParserRULE_exprList
}
func (*ExprListContext) IsExprListContext() {}
func NewExprListContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *ExprListContext {
var p = new(ExprListContext)
antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, parent, invokingState)
p.parser = parser
p.RuleIndex = CELParserRULE_exprList
return p
}
func (s *ExprListContext) GetParser() antlr.Parser { return s.parser }
func (s *ExprListContext) Get_expr() IExprContext { return s._expr }
func (s *ExprListContext) Set_expr(v IExprContext) { s._expr = v }
func (s *ExprListContext) GetE() []IExprContext { return s.e }
func (s *ExprListContext) SetE(v []IExprContext) { s.e = v }
func (s *ExprListContext) AllExpr() []IExprContext {
children := s.GetChildren()
len := 0
for _, ctx := range children {
if _, ok := ctx.(IExprContext); ok {
len++
}
}
tst := make([]IExprContext, len)
i := 0
for _, ctx := range children {
if t, ok := ctx.(IExprContext); ok {
tst[i] = t.(IExprContext)
i++
}
}
return tst
}
func (s *ExprListContext) Expr(i int) IExprContext {
var t antlr.RuleContext
j := 0
for _, ctx := range s.GetChildren() {
if _, ok := ctx.(IExprContext); ok {
if j == i {
t = ctx.(antlr.RuleContext)
break
}
j++
}
}
if t == nil {
return nil
}
return t.(IExprContext)
}
func (s *ExprListContext) AllCOMMA() []antlr.TerminalNode {
return s.GetTokens(CELParserCOMMA)
}
func (s *ExprListContext) COMMA(i int) antlr.TerminalNode {
return s.GetToken(CELParserCOMMA, i)
}
func (s *ExprListContext) GetRuleContext() antlr.RuleContext {
return s
}
func (s *ExprListContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string {
return antlr.TreesStringTree(s, ruleNames, recog)
}
func (s *ExprListContext) EnterRule(listener antlr.ParseTreeListener) {
if listenerT, ok := listener.(CELListener); ok {
listenerT.EnterExprList(s)
}
}
func (s *ExprListContext) ExitRule(listener antlr.ParseTreeListener) {
if listenerT, ok := listener.(CELListener); ok {
listenerT.ExitExprList(s)
}
}
func (s *ExprListContext) Accept(visitor antlr.ParseTreeVisitor) interface{} {
switch t := visitor.(type) {
case CELVisitor:
return t.VisitExprList(s)
default:
return t.VisitChildren(s)
}
}
func (p *CELParser) ExprList() (localctx IExprListContext) {
localctx = NewExprListContext(p, p.GetParserRuleContext(), p.GetState())
p.EnterRule(localctx, 18, CELParserRULE_exprList)
var _la int
p.EnterOuterAlt(localctx, 1)
{
p.SetState(186)
var _x = p.Expr()
localctx.(*ExprListContext)._expr = _x
}
localctx.(*ExprListContext).e = append(localctx.(*ExprListContext).e, localctx.(*ExprListContext)._expr)
p.SetState(191)
p.GetErrorHandler().Sync(p)
if p.HasError() {
goto errorExit
}
_la = p.GetTokenStream().LA(1)
for _la == CELParserCOMMA {
{
p.SetState(187)
p.Match(CELParserCOMMA)
if p.HasError() {
// Recognition error - abort rule
goto errorExit
}
}
{
p.SetState(188)
var _x = p.Expr()
localctx.(*ExprListContext)._expr = _x
}
localctx.(*ExprListContext).e = append(localctx.(*ExprListContext).e, localctx.(*ExprListContext)._expr)
p.SetState(193)
p.GetErrorHandler().Sync(p)
if p.HasError() {
goto errorExit
}
_la = p.GetTokenStream().LA(1)
}
errorExit:
if p.HasError() {
v := p.GetError()
localctx.SetException(v)
p.GetErrorHandler().ReportError(p, v)
p.GetErrorHandler().Recover(p, v)
p.SetError(nil)
}
p.ExitRule()
return localctx
goto errorExit // Trick to prevent compiler error if the label is not used
}
// IListInitContext is an interface to support dynamic dispatch.
type IListInitContext interface {
antlr.ParserRuleContext
// GetParser returns the parser.
GetParser() antlr.Parser
// Get_optExpr returns the _optExpr rule contexts.
Get_optExpr() IOptExprContext
// Set_optExpr sets the _optExpr rule contexts.
Set_optExpr(IOptExprContext)
// GetElems returns the elems rule context list.
GetElems() []IOptExprContext
// SetElems sets the elems rule context list.
SetElems([]IOptExprContext)
// Getter signatures
AllOptExpr() []IOptExprContext
OptExpr(i int) IOptExprContext
AllCOMMA() []antlr.TerminalNode
COMMA(i int) antlr.TerminalNode
// IsListInitContext differentiates from other interfaces.
IsListInitContext()
}
type ListInitContext struct {
antlr.BaseParserRuleContext
parser antlr.Parser
_optExpr IOptExprContext
elems []IOptExprContext
}
func NewEmptyListInitContext() *ListInitContext {
var p = new(ListInitContext)
antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1)
p.RuleIndex = CELParserRULE_listInit
return p
}
func InitEmptyListInitContext(p *ListInitContext) {
antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1)
p.RuleIndex = CELParserRULE_listInit
}
func (*ListInitContext) IsListInitContext() {}
func NewListInitContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *ListInitContext {
var p = new(ListInitContext)
antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, parent, invokingState)
p.parser = parser
p.RuleIndex = CELParserRULE_listInit
return p
}
func (s *ListInitContext) GetParser() antlr.Parser { return s.parser }
func (s *ListInitContext) Get_optExpr() IOptExprContext { return s._optExpr }
func (s *ListInitContext) Set_optExpr(v IOptExprContext) { s._optExpr = v }
func (s *ListInitContext) GetElems() []IOptExprContext { return s.elems }
func (s *ListInitContext) SetElems(v []IOptExprContext) { s.elems = v }
func (s *ListInitContext) AllOptExpr() []IOptExprContext {
children := s.GetChildren()
len := 0
for _, ctx := range children {
if _, ok := ctx.(IOptExprContext); ok {
len++
}
}
tst := make([]IOptExprContext, len)
i := 0
for _, ctx := range children {
if t, ok := ctx.(IOptExprContext); ok {
tst[i] = t.(IOptExprContext)
i++
}
}
return tst
}
func (s *ListInitContext) OptExpr(i int) IOptExprContext {
var t antlr.RuleContext
j := 0
for _, ctx := range s.GetChildren() {
if _, ok := ctx.(IOptExprContext); ok {
if j == i {
t = ctx.(antlr.RuleContext)
break
}
j++
}
}
if t == nil {
return nil
}
return t.(IOptExprContext)
}
func (s *ListInitContext) AllCOMMA() []antlr.TerminalNode {
return s.GetTokens(CELParserCOMMA)
}
func (s *ListInitContext) COMMA(i int) antlr.TerminalNode {
return s.GetToken(CELParserCOMMA, i)
}
func (s *ListInitContext) GetRuleContext() antlr.RuleContext {
return s
}
func (s *ListInitContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string {
return antlr.TreesStringTree(s, ruleNames, recog)
}
func (s *ListInitContext) EnterRule(listener antlr.ParseTreeListener) {
if listenerT, ok := listener.(CELListener); ok {
listenerT.EnterListInit(s)
}
}
func (s *ListInitContext) ExitRule(listener antlr.ParseTreeListener) {
if listenerT, ok := listener.(CELListener); ok {
listenerT.ExitListInit(s)
}
}
func (s *ListInitContext) Accept(visitor antlr.ParseTreeVisitor) interface{} {
switch t := visitor.(type) {
case CELVisitor:
return t.VisitListInit(s)
default:
return t.VisitChildren(s)
}
}
func (p *CELParser) ListInit() (localctx IListInitContext) {
localctx = NewListInitContext(p, p.GetParserRuleContext(), p.GetState())
p.EnterRule(localctx, 20, CELParserRULE_listInit)
var _alt int
p.EnterOuterAlt(localctx, 1)
{
p.SetState(194)
var _x = p.OptExpr()
localctx.(*ListInitContext)._optExpr = _x
}
localctx.(*ListInitContext).elems = append(localctx.(*ListInitContext).elems, localctx.(*ListInitContext)._optExpr)
p.SetState(199)
p.GetErrorHandler().Sync(p)
if p.HasError() {
goto errorExit
}
_alt = p.GetInterpreter().AdaptivePredict(p.BaseParser, p.GetTokenStream(), 27, p.GetParserRuleContext())
if p.HasError() {
goto errorExit
}
for _alt != 2 && _alt != antlr.ATNInvalidAltNumber {
if _alt == 1 {
{
p.SetState(195)
p.Match(CELParserCOMMA)
if p.HasError() {
// Recognition error - abort rule
goto errorExit
}
}
{
p.SetState(196)
var _x = p.OptExpr()
localctx.(*ListInitContext)._optExpr = _x
}
localctx.(*ListInitContext).elems = append(localctx.(*ListInitContext).elems, localctx.(*ListInitContext)._optExpr)
}
p.SetState(201)
p.GetErrorHandler().Sync(p)
if p.HasError() {
goto errorExit
}
_alt = p.GetInterpreter().AdaptivePredict(p.BaseParser, p.GetTokenStream(), 27, p.GetParserRuleContext())
if p.HasError() {
goto errorExit
}
}
errorExit:
if p.HasError() {
v := p.GetError()
localctx.SetException(v)
p.GetErrorHandler().ReportError(p, v)
p.GetErrorHandler().Recover(p, v)
p.SetError(nil)
}
p.ExitRule()
return localctx
goto errorExit // Trick to prevent compiler error if the label is not used
}
// IFieldInitializerListContext is an interface to support dynamic dispatch.
type IFieldInitializerListContext interface {
antlr.ParserRuleContext
// GetParser returns the parser.
GetParser() antlr.Parser
// GetS21 returns the s21 token.
GetS21() antlr.Token
// SetS21 sets the s21 token.
SetS21(antlr.Token)
// GetCols returns the cols token list.
GetCols() []antlr.Token
// SetCols sets the cols token list.
SetCols([]antlr.Token)
// Get_optField returns the _optField rule contexts.
Get_optField() IOptFieldContext
// Get_expr returns the _expr rule contexts.
Get_expr() IExprContext
// Set_optField sets the _optField rule contexts.
Set_optField(IOptFieldContext)
// Set_expr sets the _expr rule contexts.
Set_expr(IExprContext)
// GetFields returns the fields rule context list.
GetFields() []IOptFieldContext
// GetValues returns the values rule context list.
GetValues() []IExprContext
// SetFields sets the fields rule context list.
SetFields([]IOptFieldContext)
// SetValues sets the values rule context list.
SetValues([]IExprContext)
// Getter signatures
AllOptField() []IOptFieldContext
OptField(i int) IOptFieldContext
AllCOLON() []antlr.TerminalNode
COLON(i int) antlr.TerminalNode
AllExpr() []IExprContext
Expr(i int) IExprContext
AllCOMMA() []antlr.TerminalNode
COMMA(i int) antlr.TerminalNode
// IsFieldInitializerListContext differentiates from other interfaces.
IsFieldInitializerListContext()
}
type FieldInitializerListContext struct {
antlr.BaseParserRuleContext
parser antlr.Parser
_optField IOptFieldContext
fields []IOptFieldContext
s21 antlr.Token
cols []antlr.Token
_expr IExprContext
values []IExprContext
}
func NewEmptyFieldInitializerListContext() *FieldInitializerListContext {
var p = new(FieldInitializerListContext)
antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1)
p.RuleIndex = CELParserRULE_fieldInitializerList
return p
}
func InitEmptyFieldInitializerListContext(p *FieldInitializerListContext) {
antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1)
p.RuleIndex = CELParserRULE_fieldInitializerList
}
func (*FieldInitializerListContext) IsFieldInitializerListContext() {}
func NewFieldInitializerListContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *FieldInitializerListContext {
var p = new(FieldInitializerListContext)
antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, parent, invokingState)
p.parser = parser
p.RuleIndex = CELParserRULE_fieldInitializerList
return p
}
func (s *FieldInitializerListContext) GetParser() antlr.Parser { return s.parser }
func (s *FieldInitializerListContext) GetS21() antlr.Token { return s.s21 }
func (s *FieldInitializerListContext) SetS21(v antlr.Token) { s.s21 = v }
func (s *FieldInitializerListContext) GetCols() []antlr.Token { return s.cols }
func (s *FieldInitializerListContext) SetCols(v []antlr.Token) { s.cols = v }
func (s *FieldInitializerListContext) Get_optField() IOptFieldContext { return s._optField }
func (s *FieldInitializerListContext) Get_expr() IExprContext { return s._expr }
func (s *FieldInitializerListContext) Set_optField(v IOptFieldContext) { s._optField = v }
func (s *FieldInitializerListContext) Set_expr(v IExprContext) { s._expr = v }
func (s *FieldInitializerListContext) GetFields() []IOptFieldContext { return s.fields }
func (s *FieldInitializerListContext) GetValues() []IExprContext { return s.values }
func (s *FieldInitializerListContext) SetFields(v []IOptFieldContext) { s.fields = v }
func (s *FieldInitializerListContext) SetValues(v []IExprContext) { s.values = v }
func (s *FieldInitializerListContext) AllOptField() []IOptFieldContext {
children := s.GetChildren()
len := 0
for _, ctx := range children {
if _, ok := ctx.(IOptFieldContext); ok {
len++
}
}
tst := make([]IOptFieldContext, len)
i := 0
for _, ctx := range children {
if t, ok := ctx.(IOptFieldContext); ok {
tst[i] = t.(IOptFieldContext)
i++
}
}
return tst
}
func (s *FieldInitializerListContext) OptField(i int) IOptFieldContext {
var t antlr.RuleContext
j := 0
for _, ctx := range s.GetChildren() {
if _, ok := ctx.(IOptFieldContext); ok {
if j == i {
t = ctx.(antlr.RuleContext)
break
}
j++
}
}
if t == nil {
return nil
}
return t.(IOptFieldContext)
}
func (s *FieldInitializerListContext) AllCOLON() []antlr.TerminalNode {
return s.GetTokens(CELParserCOLON)
}
func (s *FieldInitializerListContext) COLON(i int) antlr.TerminalNode {
return s.GetToken(CELParserCOLON, i)
}
func (s *FieldInitializerListContext) AllExpr() []IExprContext {
children := s.GetChildren()
len := 0
for _, ctx := range children {
if _, ok := ctx.(IExprContext); ok {
len++
}
}
tst := make([]IExprContext, len)
i := 0
for _, ctx := range children {
if t, ok := ctx.(IExprContext); ok {
tst[i] = t.(IExprContext)
i++
}
}
return tst
}
func (s *FieldInitializerListContext) Expr(i int) IExprContext {
var t antlr.RuleContext
j := 0
for _, ctx := range s.GetChildren() {
if _, ok := ctx.(IExprContext); ok {
if j == i {
t = ctx.(antlr.RuleContext)
break
}
j++
}
}
if t == nil {
return nil
}
return t.(IExprContext)
}
func (s *FieldInitializerListContext) AllCOMMA() []antlr.TerminalNode {
return s.GetTokens(CELParserCOMMA)
}
func (s *FieldInitializerListContext) COMMA(i int) antlr.TerminalNode {
return s.GetToken(CELParserCOMMA, i)
}
func (s *FieldInitializerListContext) GetRuleContext() antlr.RuleContext {
return s
}
func (s *FieldInitializerListContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string {
return antlr.TreesStringTree(s, ruleNames, recog)
}
func (s *FieldInitializerListContext) EnterRule(listener antlr.ParseTreeListener) {
if listenerT, ok := listener.(CELListener); ok {
listenerT.EnterFieldInitializerList(s)
}
}
func (s *FieldInitializerListContext) ExitRule(listener antlr.ParseTreeListener) {
if listenerT, ok := listener.(CELListener); ok {
listenerT.ExitFieldInitializerList(s)
}
}
func (s *FieldInitializerListContext) Accept(visitor antlr.ParseTreeVisitor) interface{} {
switch t := visitor.(type) {
case CELVisitor:
return t.VisitFieldInitializerList(s)
default:
return t.VisitChildren(s)
}
}
func (p *CELParser) FieldInitializerList() (localctx IFieldInitializerListContext) {
localctx = NewFieldInitializerListContext(p, p.GetParserRuleContext(), p.GetState())
p.EnterRule(localctx, 22, CELParserRULE_fieldInitializerList)
var _alt int
p.EnterOuterAlt(localctx, 1)
{
p.SetState(202)
var _x = p.OptField()
localctx.(*FieldInitializerListContext)._optField = _x
}
localctx.(*FieldInitializerListContext).fields = append(localctx.(*FieldInitializerListContext).fields, localctx.(*FieldInitializerListContext)._optField)
{
p.SetState(203)
var _m = p.Match(CELParserCOLON)
localctx.(*FieldInitializerListContext).s21 = _m
if p.HasError() {
// Recognition error - abort rule
goto errorExit
}
}
localctx.(*FieldInitializerListContext).cols = append(localctx.(*FieldInitializerListContext).cols, localctx.(*FieldInitializerListContext).s21)
{
p.SetState(204)
var _x = p.Expr()
localctx.(*FieldInitializerListContext)._expr = _x
}
localctx.(*FieldInitializerListContext).values = append(localctx.(*FieldInitializerListContext).values, localctx.(*FieldInitializerListContext)._expr)
p.SetState(212)
p.GetErrorHandler().Sync(p)
if p.HasError() {
goto errorExit
}
_alt = p.GetInterpreter().AdaptivePredict(p.BaseParser, p.GetTokenStream(), 28, p.GetParserRuleContext())
if p.HasError() {
goto errorExit
}
for _alt != 2 && _alt != antlr.ATNInvalidAltNumber {
if _alt == 1 {
{
p.SetState(205)
p.Match(CELParserCOMMA)
if p.HasError() {
// Recognition error - abort rule
goto errorExit
}
}
{
p.SetState(206)
var _x = p.OptField()
localctx.(*FieldInitializerListContext)._optField = _x
}
localctx.(*FieldInitializerListContext).fields = append(localctx.(*FieldInitializerListContext).fields, localctx.(*FieldInitializerListContext)._optField)
{
p.SetState(207)
var _m = p.Match(CELParserCOLON)
localctx.(*FieldInitializerListContext).s21 = _m
if p.HasError() {
// Recognition error - abort rule
goto errorExit
}
}
localctx.(*FieldInitializerListContext).cols = append(localctx.(*FieldInitializerListContext).cols, localctx.(*FieldInitializerListContext).s21)
{
p.SetState(208)
var _x = p.Expr()
localctx.(*FieldInitializerListContext)._expr = _x
}
localctx.(*FieldInitializerListContext).values = append(localctx.(*FieldInitializerListContext).values, localctx.(*FieldInitializerListContext)._expr)
}
p.SetState(214)
p.GetErrorHandler().Sync(p)
if p.HasError() {
goto errorExit
}
_alt = p.GetInterpreter().AdaptivePredict(p.BaseParser, p.GetTokenStream(), 28, p.GetParserRuleContext())
if p.HasError() {
goto errorExit
}
}
errorExit:
if p.HasError() {
v := p.GetError()
localctx.SetException(v)
p.GetErrorHandler().ReportError(p, v)
p.GetErrorHandler().Recover(p, v)
p.SetError(nil)
}
p.ExitRule()
return localctx
goto errorExit // Trick to prevent compiler error if the label is not used
}
// IOptFieldContext is an interface to support dynamic dispatch.
type IOptFieldContext interface {
antlr.ParserRuleContext
// GetParser returns the parser.
GetParser() antlr.Parser
// GetOpt returns the opt token.
GetOpt() antlr.Token
// SetOpt sets the opt token.
SetOpt(antlr.Token)
// Getter signatures
EscapeIdent() IEscapeIdentContext
QUESTIONMARK() antlr.TerminalNode
// IsOptFieldContext differentiates from other interfaces.
IsOptFieldContext()
}
type OptFieldContext struct {
antlr.BaseParserRuleContext
parser antlr.Parser
opt antlr.Token
}
func NewEmptyOptFieldContext() *OptFieldContext {
var p = new(OptFieldContext)
antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1)
p.RuleIndex = CELParserRULE_optField
return p
}
func InitEmptyOptFieldContext(p *OptFieldContext) {
antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1)
p.RuleIndex = CELParserRULE_optField
}
func (*OptFieldContext) IsOptFieldContext() {}
func NewOptFieldContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *OptFieldContext {
var p = new(OptFieldContext)
antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, parent, invokingState)
p.parser = parser
p.RuleIndex = CELParserRULE_optField
return p
}
func (s *OptFieldContext) GetParser() antlr.Parser { return s.parser }
func (s *OptFieldContext) GetOpt() antlr.Token { return s.opt }
func (s *OptFieldContext) SetOpt(v antlr.Token) { s.opt = v }
func (s *OptFieldContext) EscapeIdent() IEscapeIdentContext {
var t antlr.RuleContext
for _, ctx := range s.GetChildren() {
if _, ok := ctx.(IEscapeIdentContext); ok {
t = ctx.(antlr.RuleContext)
break
}
}
if t == nil {
return nil
}
return t.(IEscapeIdentContext)
}
func (s *OptFieldContext) QUESTIONMARK() antlr.TerminalNode {
return s.GetToken(CELParserQUESTIONMARK, 0)
}
func (s *OptFieldContext) GetRuleContext() antlr.RuleContext {
return s
}
func (s *OptFieldContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string {
return antlr.TreesStringTree(s, ruleNames, recog)
}
func (s *OptFieldContext) EnterRule(listener antlr.ParseTreeListener) {
if listenerT, ok := listener.(CELListener); ok {
listenerT.EnterOptField(s)
}
}
func (s *OptFieldContext) ExitRule(listener antlr.ParseTreeListener) {
if listenerT, ok := listener.(CELListener); ok {
listenerT.ExitOptField(s)
}
}
func (s *OptFieldContext) Accept(visitor antlr.ParseTreeVisitor) interface{} {
switch t := visitor.(type) {
case CELVisitor:
return t.VisitOptField(s)
default:
return t.VisitChildren(s)
}
}
func (p *CELParser) OptField() (localctx IOptFieldContext) {
localctx = NewOptFieldContext(p, p.GetParserRuleContext(), p.GetState())
p.EnterRule(localctx, 24, CELParserRULE_optField)
var _la int
p.EnterOuterAlt(localctx, 1)
p.SetState(216)
p.GetErrorHandler().Sync(p)
if p.HasError() {
goto errorExit
}
_la = p.GetTokenStream().LA(1)
if _la == CELParserQUESTIONMARK {
{
p.SetState(215)
var _m = p.Match(CELParserQUESTIONMARK)
localctx.(*OptFieldContext).opt = _m
if p.HasError() {
// Recognition error - abort rule
goto errorExit
}
}
}
{
p.SetState(218)
p.EscapeIdent()
}
errorExit:
if p.HasError() {
v := p.GetError()
localctx.SetException(v)
p.GetErrorHandler().ReportError(p, v)
p.GetErrorHandler().Recover(p, v)
p.SetError(nil)
}
p.ExitRule()
return localctx
goto errorExit // Trick to prevent compiler error if the label is not used
}
// IMapInitializerListContext is an interface to support dynamic dispatch.
type IMapInitializerListContext interface {
antlr.ParserRuleContext
// GetParser returns the parser.
GetParser() antlr.Parser
// GetS21 returns the s21 token.
GetS21() antlr.Token
// SetS21 sets the s21 token.
SetS21(antlr.Token)
// GetCols returns the cols token list.
GetCols() []antlr.Token
// SetCols sets the cols token list.
SetCols([]antlr.Token)
// Get_optExpr returns the _optExpr rule contexts.
Get_optExpr() IOptExprContext
// Get_expr returns the _expr rule contexts.
Get_expr() IExprContext
// Set_optExpr sets the _optExpr rule contexts.
Set_optExpr(IOptExprContext)
// Set_expr sets the _expr rule contexts.
Set_expr(IExprContext)
// GetKeys returns the keys rule context list.
GetKeys() []IOptExprContext
// GetValues returns the values rule context list.
GetValues() []IExprContext
// SetKeys sets the keys rule context list.
SetKeys([]IOptExprContext)
// SetValues sets the values rule context list.
SetValues([]IExprContext)
// Getter signatures
AllOptExpr() []IOptExprContext
OptExpr(i int) IOptExprContext
AllCOLON() []antlr.TerminalNode
COLON(i int) antlr.TerminalNode
AllExpr() []IExprContext
Expr(i int) IExprContext
AllCOMMA() []antlr.TerminalNode
COMMA(i int) antlr.TerminalNode
// IsMapInitializerListContext differentiates from other interfaces.
IsMapInitializerListContext()
}
type MapInitializerListContext struct {
antlr.BaseParserRuleContext
parser antlr.Parser
_optExpr IOptExprContext
keys []IOptExprContext
s21 antlr.Token
cols []antlr.Token
_expr IExprContext
values []IExprContext
}
func NewEmptyMapInitializerListContext() *MapInitializerListContext {
var p = new(MapInitializerListContext)
antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1)
p.RuleIndex = CELParserRULE_mapInitializerList
return p
}
func InitEmptyMapInitializerListContext(p *MapInitializerListContext) {
antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1)
p.RuleIndex = CELParserRULE_mapInitializerList
}
func (*MapInitializerListContext) IsMapInitializerListContext() {}
func NewMapInitializerListContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *MapInitializerListContext {
var p = new(MapInitializerListContext)
antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, parent, invokingState)
p.parser = parser
p.RuleIndex = CELParserRULE_mapInitializerList
return p
}
func (s *MapInitializerListContext) GetParser() antlr.Parser { return s.parser }
func (s *MapInitializerListContext) GetS21() antlr.Token { return s.s21 }
func (s *MapInitializerListContext) SetS21(v antlr.Token) { s.s21 = v }
func (s *MapInitializerListContext) GetCols() []antlr.Token { return s.cols }
func (s *MapInitializerListContext) SetCols(v []antlr.Token) { s.cols = v }
func (s *MapInitializerListContext) Get_optExpr() IOptExprContext { return s._optExpr }
func (s *MapInitializerListContext) Get_expr() IExprContext { return s._expr }
func (s *MapInitializerListContext) Set_optExpr(v IOptExprContext) { s._optExpr = v }
func (s *MapInitializerListContext) Set_expr(v IExprContext) { s._expr = v }
func (s *MapInitializerListContext) GetKeys() []IOptExprContext { return s.keys }
func (s *MapInitializerListContext) GetValues() []IExprContext { return s.values }
func (s *MapInitializerListContext) SetKeys(v []IOptExprContext) { s.keys = v }
func (s *MapInitializerListContext) SetValues(v []IExprContext) { s.values = v }
func (s *MapInitializerListContext) AllOptExpr() []IOptExprContext {
children := s.GetChildren()
len := 0
for _, ctx := range children {
if _, ok := ctx.(IOptExprContext); ok {
len++
}
}
tst := make([]IOptExprContext, len)
i := 0
for _, ctx := range children {
if t, ok := ctx.(IOptExprContext); ok {
tst[i] = t.(IOptExprContext)
i++
}
}
return tst
}
func (s *MapInitializerListContext) OptExpr(i int) IOptExprContext {
var t antlr.RuleContext
j := 0
for _, ctx := range s.GetChildren() {
if _, ok := ctx.(IOptExprContext); ok {
if j == i {
t = ctx.(antlr.RuleContext)
break
}
j++
}
}
if t == nil {
return nil
}
return t.(IOptExprContext)
}
func (s *MapInitializerListContext) AllCOLON() []antlr.TerminalNode {
return s.GetTokens(CELParserCOLON)
}
func (s *MapInitializerListContext) COLON(i int) antlr.TerminalNode {
return s.GetToken(CELParserCOLON, i)
}
func (s *MapInitializerListContext) AllExpr() []IExprContext {
children := s.GetChildren()
len := 0
for _, ctx := range children {
if _, ok := ctx.(IExprContext); ok {
len++
}
}
tst := make([]IExprContext, len)
i := 0
for _, ctx := range children {
if t, ok := ctx.(IExprContext); ok {
tst[i] = t.(IExprContext)
i++
}
}
return tst
}
func (s *MapInitializerListContext) Expr(i int) IExprContext {
var t antlr.RuleContext
j := 0
for _, ctx := range s.GetChildren() {
if _, ok := ctx.(IExprContext); ok {
if j == i {
t = ctx.(antlr.RuleContext)
break
}
j++
}
}
if t == nil {
return nil
}
return t.(IExprContext)
}
func (s *MapInitializerListContext) AllCOMMA() []antlr.TerminalNode {
return s.GetTokens(CELParserCOMMA)
}
func (s *MapInitializerListContext) COMMA(i int) antlr.TerminalNode {
return s.GetToken(CELParserCOMMA, i)
}
func (s *MapInitializerListContext) GetRuleContext() antlr.RuleContext {
return s
}
func (s *MapInitializerListContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string {
return antlr.TreesStringTree(s, ruleNames, recog)
}
func (s *MapInitializerListContext) EnterRule(listener antlr.ParseTreeListener) {
if listenerT, ok := listener.(CELListener); ok {
listenerT.EnterMapInitializerList(s)
}
}
func (s *MapInitializerListContext) ExitRule(listener antlr.ParseTreeListener) {
if listenerT, ok := listener.(CELListener); ok {
listenerT.ExitMapInitializerList(s)
}
}
func (s *MapInitializerListContext) Accept(visitor antlr.ParseTreeVisitor) interface{} {
switch t := visitor.(type) {
case CELVisitor:
return t.VisitMapInitializerList(s)
default:
return t.VisitChildren(s)
}
}
func (p *CELParser) MapInitializerList() (localctx IMapInitializerListContext) {
localctx = NewMapInitializerListContext(p, p.GetParserRuleContext(), p.GetState())
p.EnterRule(localctx, 26, CELParserRULE_mapInitializerList)
var _alt int
p.EnterOuterAlt(localctx, 1)
{
p.SetState(220)
var _x = p.OptExpr()
localctx.(*MapInitializerListContext)._optExpr = _x
}
localctx.(*MapInitializerListContext).keys = append(localctx.(*MapInitializerListContext).keys, localctx.(*MapInitializerListContext)._optExpr)
{
p.SetState(221)
var _m = p.Match(CELParserCOLON)
localctx.(*MapInitializerListContext).s21 = _m
if p.HasError() {
// Recognition error - abort rule
goto errorExit
}
}
localctx.(*MapInitializerListContext).cols = append(localctx.(*MapInitializerListContext).cols, localctx.(*MapInitializerListContext).s21)
{
p.SetState(222)
var _x = p.Expr()
localctx.(*MapInitializerListContext)._expr = _x
}
localctx.(*MapInitializerListContext).values = append(localctx.(*MapInitializerListContext).values, localctx.(*MapInitializerListContext)._expr)
p.SetState(230)
p.GetErrorHandler().Sync(p)
if p.HasError() {
goto errorExit
}
_alt = p.GetInterpreter().AdaptivePredict(p.BaseParser, p.GetTokenStream(), 30, p.GetParserRuleContext())
if p.HasError() {
goto errorExit
}
for _alt != 2 && _alt != antlr.ATNInvalidAltNumber {
if _alt == 1 {
{
p.SetState(223)
p.Match(CELParserCOMMA)
if p.HasError() {
// Recognition error - abort rule
goto errorExit
}
}
{
p.SetState(224)
var _x = p.OptExpr()
localctx.(*MapInitializerListContext)._optExpr = _x
}
localctx.(*MapInitializerListContext).keys = append(localctx.(*MapInitializerListContext).keys, localctx.(*MapInitializerListContext)._optExpr)
{
p.SetState(225)
var _m = p.Match(CELParserCOLON)
localctx.(*MapInitializerListContext).s21 = _m
if p.HasError() {
// Recognition error - abort rule
goto errorExit
}
}
localctx.(*MapInitializerListContext).cols = append(localctx.(*MapInitializerListContext).cols, localctx.(*MapInitializerListContext).s21)
{
p.SetState(226)
var _x = p.Expr()
localctx.(*MapInitializerListContext)._expr = _x
}
localctx.(*MapInitializerListContext).values = append(localctx.(*MapInitializerListContext).values, localctx.(*MapInitializerListContext)._expr)
}
p.SetState(232)
p.GetErrorHandler().Sync(p)
if p.HasError() {
goto errorExit
}
_alt = p.GetInterpreter().AdaptivePredict(p.BaseParser, p.GetTokenStream(), 30, p.GetParserRuleContext())
if p.HasError() {
goto errorExit
}
}
errorExit:
if p.HasError() {
v := p.GetError()
localctx.SetException(v)
p.GetErrorHandler().ReportError(p, v)
p.GetErrorHandler().Recover(p, v)
p.SetError(nil)
}
p.ExitRule()
return localctx
goto errorExit // Trick to prevent compiler error if the label is not used
}
// IEscapeIdentContext is an interface to support dynamic dispatch.
type IEscapeIdentContext interface {
antlr.ParserRuleContext
// GetParser returns the parser.
GetParser() antlr.Parser
// IsEscapeIdentContext differentiates from other interfaces.
IsEscapeIdentContext()
}
type EscapeIdentContext struct {
antlr.BaseParserRuleContext
parser antlr.Parser
}
func NewEmptyEscapeIdentContext() *EscapeIdentContext {
var p = new(EscapeIdentContext)
antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1)
p.RuleIndex = CELParserRULE_escapeIdent
return p
}
func InitEmptyEscapeIdentContext(p *EscapeIdentContext) {
antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1)
p.RuleIndex = CELParserRULE_escapeIdent
}
func (*EscapeIdentContext) IsEscapeIdentContext() {}
func NewEscapeIdentContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *EscapeIdentContext {
var p = new(EscapeIdentContext)
antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, parent, invokingState)
p.parser = parser
p.RuleIndex = CELParserRULE_escapeIdent
return p
}
func (s *EscapeIdentContext) GetParser() antlr.Parser { return s.parser }
func (s *EscapeIdentContext) CopyAll(ctx *EscapeIdentContext) {
s.CopyFrom(&ctx.BaseParserRuleContext)
}
func (s *EscapeIdentContext) GetRuleContext() antlr.RuleContext {
return s
}
func (s *EscapeIdentContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string {
return antlr.TreesStringTree(s, ruleNames, recog)
}
type EscapedIdentifierContext struct {
EscapeIdentContext
id antlr.Token
}
func NewEscapedIdentifierContext(parser antlr.Parser, ctx antlr.ParserRuleContext) *EscapedIdentifierContext {
var p = new(EscapedIdentifierContext)
InitEmptyEscapeIdentContext(&p.EscapeIdentContext)
p.parser = parser
p.CopyAll(ctx.(*EscapeIdentContext))
return p
}
func (s *EscapedIdentifierContext) GetId() antlr.Token { return s.id }
func (s *EscapedIdentifierContext) SetId(v antlr.Token) { s.id = v }
func (s *EscapedIdentifierContext) GetRuleContext() antlr.RuleContext {
return s
}
func (s *EscapedIdentifierContext) ESC_IDENTIFIER() antlr.TerminalNode {
return s.GetToken(CELParserESC_IDENTIFIER, 0)
}
func (s *EscapedIdentifierContext) EnterRule(listener antlr.ParseTreeListener) {
if listenerT, ok := listener.(CELListener); ok {
listenerT.EnterEscapedIdentifier(s)
}
}
func (s *EscapedIdentifierContext) ExitRule(listener antlr.ParseTreeListener) {
if listenerT, ok := listener.(CELListener); ok {
listenerT.ExitEscapedIdentifier(s)
}
}
func (s *EscapedIdentifierContext) Accept(visitor antlr.ParseTreeVisitor) interface{} {
switch t := visitor.(type) {
case CELVisitor:
return t.VisitEscapedIdentifier(s)
default:
return t.VisitChildren(s)
}
}
type SimpleIdentifierContext struct {
EscapeIdentContext
id antlr.Token
}
func NewSimpleIdentifierContext(parser antlr.Parser, ctx antlr.ParserRuleContext) *SimpleIdentifierContext {
var p = new(SimpleIdentifierContext)
InitEmptyEscapeIdentContext(&p.EscapeIdentContext)
p.parser = parser
p.CopyAll(ctx.(*EscapeIdentContext))
return p
}
func (s *SimpleIdentifierContext) GetId() antlr.Token { return s.id }
func (s *SimpleIdentifierContext) SetId(v antlr.Token) { s.id = v }
func (s *SimpleIdentifierContext) GetRuleContext() antlr.RuleContext {
return s
}
func (s *SimpleIdentifierContext) IDENTIFIER() antlr.TerminalNode {
return s.GetToken(CELParserIDENTIFIER, 0)
}
func (s *SimpleIdentifierContext) EnterRule(listener antlr.ParseTreeListener) {
if listenerT, ok := listener.(CELListener); ok {
listenerT.EnterSimpleIdentifier(s)
}
}
func (s *SimpleIdentifierContext) ExitRule(listener antlr.ParseTreeListener) {
if listenerT, ok := listener.(CELListener); ok {
listenerT.ExitSimpleIdentifier(s)
}
}
func (s *SimpleIdentifierContext) Accept(visitor antlr.ParseTreeVisitor) interface{} {
switch t := visitor.(type) {
case CELVisitor:
return t.VisitSimpleIdentifier(s)
default:
return t.VisitChildren(s)
}
}
func (p *CELParser) EscapeIdent() (localctx IEscapeIdentContext) {
localctx = NewEscapeIdentContext(p, p.GetParserRuleContext(), p.GetState())
p.EnterRule(localctx, 28, CELParserRULE_escapeIdent)
p.SetState(235)
p.GetErrorHandler().Sync(p)
if p.HasError() {
goto errorExit
}
switch p.GetTokenStream().LA(1) {
case CELParserIDENTIFIER:
localctx = NewSimpleIdentifierContext(p, localctx)
p.EnterOuterAlt(localctx, 1)
{
p.SetState(233)
var _m = p.Match(CELParserIDENTIFIER)
localctx.(*SimpleIdentifierContext).id = _m
if p.HasError() {
// Recognition error - abort rule
goto errorExit
}
}
case CELParserESC_IDENTIFIER:
localctx = NewEscapedIdentifierContext(p, localctx)
p.EnterOuterAlt(localctx, 2)
{
p.SetState(234)
var _m = p.Match(CELParserESC_IDENTIFIER)
localctx.(*EscapedIdentifierContext).id = _m
if p.HasError() {
// Recognition error - abort rule
goto errorExit
}
}
default:
p.SetError(antlr.NewNoViableAltException(p, nil, nil, nil, nil, nil))
goto errorExit
}
errorExit:
if p.HasError() {
v := p.GetError()
localctx.SetException(v)
p.GetErrorHandler().ReportError(p, v)
p.GetErrorHandler().Recover(p, v)
p.SetError(nil)
}
p.ExitRule()
return localctx
goto errorExit // Trick to prevent compiler error if the label is not used
}
// IOptExprContext is an interface to support dynamic dispatch.
type IOptExprContext interface {
antlr.ParserRuleContext
// GetParser returns the parser.
GetParser() antlr.Parser
// GetOpt returns the opt token.
GetOpt() antlr.Token
// SetOpt sets the opt token.
SetOpt(antlr.Token)
// GetE returns the e rule contexts.
GetE() IExprContext
// SetE sets the e rule contexts.
SetE(IExprContext)
// Getter signatures
Expr() IExprContext
QUESTIONMARK() antlr.TerminalNode
// IsOptExprContext differentiates from other interfaces.
IsOptExprContext()
}
type OptExprContext struct {
antlr.BaseParserRuleContext
parser antlr.Parser
opt antlr.Token
e IExprContext
}
func NewEmptyOptExprContext() *OptExprContext {
var p = new(OptExprContext)
antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1)
p.RuleIndex = CELParserRULE_optExpr
return p
}
func InitEmptyOptExprContext(p *OptExprContext) {
antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1)
p.RuleIndex = CELParserRULE_optExpr
}
func (*OptExprContext) IsOptExprContext() {}
func NewOptExprContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *OptExprContext {
var p = new(OptExprContext)
antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, parent, invokingState)
p.parser = parser
p.RuleIndex = CELParserRULE_optExpr
return p
}
func (s *OptExprContext) GetParser() antlr.Parser { return s.parser }
func (s *OptExprContext) GetOpt() antlr.Token { return s.opt }
func (s *OptExprContext) SetOpt(v antlr.Token) { s.opt = v }
func (s *OptExprContext) GetE() IExprContext { return s.e }
func (s *OptExprContext) SetE(v IExprContext) { s.e = v }
func (s *OptExprContext) Expr() IExprContext {
var t antlr.RuleContext
for _, ctx := range s.GetChildren() {
if _, ok := ctx.(IExprContext); ok {
t = ctx.(antlr.RuleContext)
break
}
}
if t == nil {
return nil
}
return t.(IExprContext)
}
func (s *OptExprContext) QUESTIONMARK() antlr.TerminalNode {
return s.GetToken(CELParserQUESTIONMARK, 0)
}
func (s *OptExprContext) GetRuleContext() antlr.RuleContext {
return s
}
func (s *OptExprContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string {
return antlr.TreesStringTree(s, ruleNames, recog)
}
func (s *OptExprContext) EnterRule(listener antlr.ParseTreeListener) {
if listenerT, ok := listener.(CELListener); ok {
listenerT.EnterOptExpr(s)
}
}
func (s *OptExprContext) ExitRule(listener antlr.ParseTreeListener) {
if listenerT, ok := listener.(CELListener); ok {
listenerT.ExitOptExpr(s)
}
}
func (s *OptExprContext) Accept(visitor antlr.ParseTreeVisitor) interface{} {
switch t := visitor.(type) {
case CELVisitor:
return t.VisitOptExpr(s)
default:
return t.VisitChildren(s)
}
}
func (p *CELParser) OptExpr() (localctx IOptExprContext) {
localctx = NewOptExprContext(p, p.GetParserRuleContext(), p.GetState())
p.EnterRule(localctx, 30, CELParserRULE_optExpr)
var _la int
p.EnterOuterAlt(localctx, 1)
p.SetState(238)
p.GetErrorHandler().Sync(p)
if p.HasError() {
goto errorExit
}
_la = p.GetTokenStream().LA(1)
if _la == CELParserQUESTIONMARK {
{
p.SetState(237)
var _m = p.Match(CELParserQUESTIONMARK)
localctx.(*OptExprContext).opt = _m
if p.HasError() {
// Recognition error - abort rule
goto errorExit
}
}
}
{
p.SetState(240)
var _x = p.Expr()
localctx.(*OptExprContext).e = _x
}
errorExit:
if p.HasError() {
v := p.GetError()
localctx.SetException(v)
p.GetErrorHandler().ReportError(p, v)
p.GetErrorHandler().Recover(p, v)
p.SetError(nil)
}
p.ExitRule()
return localctx
goto errorExit // Trick to prevent compiler error if the label is not used
}
// ILiteralContext is an interface to support dynamic dispatch.
type ILiteralContext interface {
antlr.ParserRuleContext
// GetParser returns the parser.
GetParser() antlr.Parser
// IsLiteralContext differentiates from other interfaces.
IsLiteralContext()
}
type LiteralContext struct {
antlr.BaseParserRuleContext
parser antlr.Parser
}
func NewEmptyLiteralContext() *LiteralContext {
var p = new(LiteralContext)
antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1)
p.RuleIndex = CELParserRULE_literal
return p
}
func InitEmptyLiteralContext(p *LiteralContext) {
antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1)
p.RuleIndex = CELParserRULE_literal
}
func (*LiteralContext) IsLiteralContext() {}
func NewLiteralContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *LiteralContext {
var p = new(LiteralContext)
antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, parent, invokingState)
p.parser = parser
p.RuleIndex = CELParserRULE_literal
return p
}
func (s *LiteralContext) GetParser() antlr.Parser { return s.parser }
func (s *LiteralContext) CopyAll(ctx *LiteralContext) {
s.CopyFrom(&ctx.BaseParserRuleContext)
}
func (s *LiteralContext) GetRuleContext() antlr.RuleContext {
return s
}
func (s *LiteralContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string {
return antlr.TreesStringTree(s, ruleNames, recog)
}
type BytesContext struct {
LiteralContext
tok antlr.Token
}
func NewBytesContext(parser antlr.Parser, ctx antlr.ParserRuleContext) *BytesContext {
var p = new(BytesContext)
InitEmptyLiteralContext(&p.LiteralContext)
p.parser = parser
p.CopyAll(ctx.(*LiteralContext))
return p
}
func (s *BytesContext) GetTok() antlr.Token { return s.tok }
func (s *BytesContext) SetTok(v antlr.Token) { s.tok = v }
func (s *BytesContext) GetRuleContext() antlr.RuleContext {
return s
}
func (s *BytesContext) BYTES() antlr.TerminalNode {
return s.GetToken(CELParserBYTES, 0)
}
func (s *BytesContext) EnterRule(listener antlr.ParseTreeListener) {
if listenerT, ok := listener.(CELListener); ok {
listenerT.EnterBytes(s)
}
}
func (s *BytesContext) ExitRule(listener antlr.ParseTreeListener) {
if listenerT, ok := listener.(CELListener); ok {
listenerT.ExitBytes(s)
}
}
func (s *BytesContext) Accept(visitor antlr.ParseTreeVisitor) interface{} {
switch t := visitor.(type) {
case CELVisitor:
return t.VisitBytes(s)
default:
return t.VisitChildren(s)
}
}
type UintContext struct {
LiteralContext
tok antlr.Token
}
func NewUintContext(parser antlr.Parser, ctx antlr.ParserRuleContext) *UintContext {
var p = new(UintContext)
InitEmptyLiteralContext(&p.LiteralContext)
p.parser = parser
p.CopyAll(ctx.(*LiteralContext))
return p
}
func (s *UintContext) GetTok() antlr.Token { return s.tok }
func (s *UintContext) SetTok(v antlr.Token) { s.tok = v }
func (s *UintContext) GetRuleContext() antlr.RuleContext {
return s
}
func (s *UintContext) NUM_UINT() antlr.TerminalNode {
return s.GetToken(CELParserNUM_UINT, 0)
}
func (s *UintContext) EnterRule(listener antlr.ParseTreeListener) {
if listenerT, ok := listener.(CELListener); ok {
listenerT.EnterUint(s)
}
}
func (s *UintContext) ExitRule(listener antlr.ParseTreeListener) {
if listenerT, ok := listener.(CELListener); ok {
listenerT.ExitUint(s)
}
}
func (s *UintContext) Accept(visitor antlr.ParseTreeVisitor) interface{} {
switch t := visitor.(type) {
case CELVisitor:
return t.VisitUint(s)
default:
return t.VisitChildren(s)
}
}
type NullContext struct {
LiteralContext
tok antlr.Token
}
func NewNullContext(parser antlr.Parser, ctx antlr.ParserRuleContext) *NullContext {
var p = new(NullContext)
InitEmptyLiteralContext(&p.LiteralContext)
p.parser = parser
p.CopyAll(ctx.(*LiteralContext))
return p
}
func (s *NullContext) GetTok() antlr.Token { return s.tok }
func (s *NullContext) SetTok(v antlr.Token) { s.tok = v }
func (s *NullContext) GetRuleContext() antlr.RuleContext {
return s
}
func (s *NullContext) NUL() antlr.TerminalNode {
return s.GetToken(CELParserNUL, 0)
}
func (s *NullContext) EnterRule(listener antlr.ParseTreeListener) {
if listenerT, ok := listener.(CELListener); ok {
listenerT.EnterNull(s)
}
}
func (s *NullContext) ExitRule(listener antlr.ParseTreeListener) {
if listenerT, ok := listener.(CELListener); ok {
listenerT.ExitNull(s)
}
}
func (s *NullContext) Accept(visitor antlr.ParseTreeVisitor) interface{} {
switch t := visitor.(type) {
case CELVisitor:
return t.VisitNull(s)
default:
return t.VisitChildren(s)
}
}
type BoolFalseContext struct {
LiteralContext
tok antlr.Token
}
func NewBoolFalseContext(parser antlr.Parser, ctx antlr.ParserRuleContext) *BoolFalseContext {
var p = new(BoolFalseContext)
InitEmptyLiteralContext(&p.LiteralContext)
p.parser = parser
p.CopyAll(ctx.(*LiteralContext))
return p
}
func (s *BoolFalseContext) GetTok() antlr.Token { return s.tok }
func (s *BoolFalseContext) SetTok(v antlr.Token) { s.tok = v }
func (s *BoolFalseContext) GetRuleContext() antlr.RuleContext {
return s
}
func (s *BoolFalseContext) CEL_FALSE() antlr.TerminalNode {
return s.GetToken(CELParserCEL_FALSE, 0)
}
func (s *BoolFalseContext) EnterRule(listener antlr.ParseTreeListener) {
if listenerT, ok := listener.(CELListener); ok {
listenerT.EnterBoolFalse(s)
}
}
func (s *BoolFalseContext) ExitRule(listener antlr.ParseTreeListener) {
if listenerT, ok := listener.(CELListener); ok {
listenerT.ExitBoolFalse(s)
}
}
func (s *BoolFalseContext) Accept(visitor antlr.ParseTreeVisitor) interface{} {
switch t := visitor.(type) {
case CELVisitor:
return t.VisitBoolFalse(s)
default:
return t.VisitChildren(s)
}
}
type StringContext struct {
LiteralContext
tok antlr.Token
}
func NewStringContext(parser antlr.Parser, ctx antlr.ParserRuleContext) *StringContext {
var p = new(StringContext)
InitEmptyLiteralContext(&p.LiteralContext)
p.parser = parser
p.CopyAll(ctx.(*LiteralContext))
return p
}
func (s *StringContext) GetTok() antlr.Token { return s.tok }
func (s *StringContext) SetTok(v antlr.Token) { s.tok = v }
func (s *StringContext) GetRuleContext() antlr.RuleContext {
return s
}
func (s *StringContext) STRING() antlr.TerminalNode {
return s.GetToken(CELParserSTRING, 0)
}
func (s *StringContext) EnterRule(listener antlr.ParseTreeListener) {
if listenerT, ok := listener.(CELListener); ok {
listenerT.EnterString(s)
}
}
func (s *StringContext) ExitRule(listener antlr.ParseTreeListener) {
if listenerT, ok := listener.(CELListener); ok {
listenerT.ExitString(s)
}
}
func (s *StringContext) Accept(visitor antlr.ParseTreeVisitor) interface{} {
switch t := visitor.(type) {
case CELVisitor:
return t.VisitString(s)
default:
return t.VisitChildren(s)
}
}
type DoubleContext struct {
LiteralContext
sign antlr.Token
tok antlr.Token
}
func NewDoubleContext(parser antlr.Parser, ctx antlr.ParserRuleContext) *DoubleContext {
var p = new(DoubleContext)
InitEmptyLiteralContext(&p.LiteralContext)
p.parser = parser
p.CopyAll(ctx.(*LiteralContext))
return p
}
func (s *DoubleContext) GetSign() antlr.Token { return s.sign }
func (s *DoubleContext) GetTok() antlr.Token { return s.tok }
func (s *DoubleContext) SetSign(v antlr.Token) { s.sign = v }
func (s *DoubleContext) SetTok(v antlr.Token) { s.tok = v }
func (s *DoubleContext) GetRuleContext() antlr.RuleContext {
return s
}
func (s *DoubleContext) NUM_FLOAT() antlr.TerminalNode {
return s.GetToken(CELParserNUM_FLOAT, 0)
}
func (s *DoubleContext) MINUS() antlr.TerminalNode {
return s.GetToken(CELParserMINUS, 0)
}
func (s *DoubleContext) EnterRule(listener antlr.ParseTreeListener) {
if listenerT, ok := listener.(CELListener); ok {
listenerT.EnterDouble(s)
}
}
func (s *DoubleContext) ExitRule(listener antlr.ParseTreeListener) {
if listenerT, ok := listener.(CELListener); ok {
listenerT.ExitDouble(s)
}
}
func (s *DoubleContext) Accept(visitor antlr.ParseTreeVisitor) interface{} {
switch t := visitor.(type) {
case CELVisitor:
return t.VisitDouble(s)
default:
return t.VisitChildren(s)
}
}
type BoolTrueContext struct {
LiteralContext
tok antlr.Token
}
func NewBoolTrueContext(parser antlr.Parser, ctx antlr.ParserRuleContext) *BoolTrueContext {
var p = new(BoolTrueContext)
InitEmptyLiteralContext(&p.LiteralContext)
p.parser = parser
p.CopyAll(ctx.(*LiteralContext))
return p
}
func (s *BoolTrueContext) GetTok() antlr.Token { return s.tok }
func (s *BoolTrueContext) SetTok(v antlr.Token) { s.tok = v }
func (s *BoolTrueContext) GetRuleContext() antlr.RuleContext {
return s
}
func (s *BoolTrueContext) CEL_TRUE() antlr.TerminalNode {
return s.GetToken(CELParserCEL_TRUE, 0)
}
func (s *BoolTrueContext) EnterRule(listener antlr.ParseTreeListener) {
if listenerT, ok := listener.(CELListener); ok {
listenerT.EnterBoolTrue(s)
}
}
func (s *BoolTrueContext) ExitRule(listener antlr.ParseTreeListener) {
if listenerT, ok := listener.(CELListener); ok {
listenerT.ExitBoolTrue(s)
}
}
func (s *BoolTrueContext) Accept(visitor antlr.ParseTreeVisitor) interface{} {
switch t := visitor.(type) {
case CELVisitor:
return t.VisitBoolTrue(s)
default:
return t.VisitChildren(s)
}
}
type IntContext struct {
LiteralContext
sign antlr.Token
tok antlr.Token
}
func NewIntContext(parser antlr.Parser, ctx antlr.ParserRuleContext) *IntContext {
var p = new(IntContext)
InitEmptyLiteralContext(&p.LiteralContext)
p.parser = parser
p.CopyAll(ctx.(*LiteralContext))
return p
}
func (s *IntContext) GetSign() antlr.Token { return s.sign }
func (s *IntContext) GetTok() antlr.Token { return s.tok }
func (s *IntContext) SetSign(v antlr.Token) { s.sign = v }
func (s *IntContext) SetTok(v antlr.Token) { s.tok = v }
func (s *IntContext) GetRuleContext() antlr.RuleContext {
return s
}
func (s *IntContext) NUM_INT() antlr.TerminalNode {
return s.GetToken(CELParserNUM_INT, 0)
}
func (s *IntContext) MINUS() antlr.TerminalNode {
return s.GetToken(CELParserMINUS, 0)
}
func (s *IntContext) EnterRule(listener antlr.ParseTreeListener) {
if listenerT, ok := listener.(CELListener); ok {
listenerT.EnterInt(s)
}
}
func (s *IntContext) ExitRule(listener antlr.ParseTreeListener) {
if listenerT, ok := listener.(CELListener); ok {
listenerT.ExitInt(s)
}
}
func (s *IntContext) Accept(visitor antlr.ParseTreeVisitor) interface{} {
switch t := visitor.(type) {
case CELVisitor:
return t.VisitInt(s)
default:
return t.VisitChildren(s)
}
}
func (p *CELParser) Literal() (localctx ILiteralContext) {
localctx = NewLiteralContext(p, p.GetParserRuleContext(), p.GetState())
p.EnterRule(localctx, 32, CELParserRULE_literal)
var _la int
p.SetState(256)
p.GetErrorHandler().Sync(p)
if p.HasError() {
goto errorExit
}
switch p.GetInterpreter().AdaptivePredict(p.BaseParser, p.GetTokenStream(), 35, p.GetParserRuleContext()) {
case 1:
localctx = NewIntContext(p, localctx)
p.EnterOuterAlt(localctx, 1)
p.SetState(243)
p.GetErrorHandler().Sync(p)
if p.HasError() {
goto errorExit
}
_la = p.GetTokenStream().LA(1)
if _la == CELParserMINUS {
{
p.SetState(242)
var _m = p.Match(CELParserMINUS)
localctx.(*IntContext).sign = _m
if p.HasError() {
// Recognition error - abort rule
goto errorExit
}
}
}
{
p.SetState(245)
var _m = p.Match(CELParserNUM_INT)
localctx.(*IntContext).tok = _m
if p.HasError() {
// Recognition error - abort rule
goto errorExit
}
}
case 2:
localctx = NewUintContext(p, localctx)
p.EnterOuterAlt(localctx, 2)
{
p.SetState(246)
var _m = p.Match(CELParserNUM_UINT)
localctx.(*UintContext).tok = _m
if p.HasError() {
// Recognition error - abort rule
goto errorExit
}
}
case 3:
localctx = NewDoubleContext(p, localctx)
p.EnterOuterAlt(localctx, 3)
p.SetState(248)
p.GetErrorHandler().Sync(p)
if p.HasError() {
goto errorExit
}
_la = p.GetTokenStream().LA(1)
if _la == CELParserMINUS {
{
p.SetState(247)
var _m = p.Match(CELParserMINUS)
localctx.(*DoubleContext).sign = _m
if p.HasError() {
// Recognition error - abort rule
goto errorExit
}
}
}
{
p.SetState(250)
var _m = p.Match(CELParserNUM_FLOAT)
localctx.(*DoubleContext).tok = _m
if p.HasError() {
// Recognition error - abort rule
goto errorExit
}
}
case 4:
localctx = NewStringContext(p, localctx)
p.EnterOuterAlt(localctx, 4)
{
p.SetState(251)
var _m = p.Match(CELParserSTRING)
localctx.(*StringContext).tok = _m
if p.HasError() {
// Recognition error - abort rule
goto errorExit
}
}
case 5:
localctx = NewBytesContext(p, localctx)
p.EnterOuterAlt(localctx, 5)
{
p.SetState(252)
var _m = p.Match(CELParserBYTES)
localctx.(*BytesContext).tok = _m
if p.HasError() {
// Recognition error - abort rule
goto errorExit
}
}
case 6:
localctx = NewBoolTrueContext(p, localctx)
p.EnterOuterAlt(localctx, 6)
{
p.SetState(253)
var _m = p.Match(CELParserCEL_TRUE)
localctx.(*BoolTrueContext).tok = _m
if p.HasError() {
// Recognition error - abort rule
goto errorExit
}
}
case 7:
localctx = NewBoolFalseContext(p, localctx)
p.EnterOuterAlt(localctx, 7)
{
p.SetState(254)
var _m = p.Match(CELParserCEL_FALSE)
localctx.(*BoolFalseContext).tok = _m
if p.HasError() {
// Recognition error - abort rule
goto errorExit
}
}
case 8:
localctx = NewNullContext(p, localctx)
p.EnterOuterAlt(localctx, 8)
{
p.SetState(255)
var _m = p.Match(CELParserNUL)
localctx.(*NullContext).tok = _m
if p.HasError() {
// Recognition error - abort rule
goto errorExit
}
}
case antlr.ATNInvalidAltNumber:
goto errorExit
}
errorExit:
if p.HasError() {
v := p.GetError()
localctx.SetException(v)
p.GetErrorHandler().ReportError(p, v)
p.GetErrorHandler().Recover(p, v)
p.SetError(nil)
}
p.ExitRule()
return localctx
goto errorExit // Trick to prevent compiler error if the label is not used
}
func (p *CELParser) Sempred(localctx antlr.RuleContext, ruleIndex, predIndex int) bool {
switch ruleIndex {
case 4:
var t *RelationContext = nil
if localctx != nil {
t = localctx.(*RelationContext)
}
return p.Relation_Sempred(t, predIndex)
case 5:
var t *CalcContext = nil
if localctx != nil {
t = localctx.(*CalcContext)
}
return p.Calc_Sempred(t, predIndex)
case 7:
var t *MemberContext = nil
if localctx != nil {
t = localctx.(*MemberContext)
}
return p.Member_Sempred(t, predIndex)
default:
panic("No predicate with index: " + fmt.Sprint(ruleIndex))
}
}
func (p *CELParser) Relation_Sempred(localctx antlr.RuleContext, predIndex int) bool {
switch predIndex {
case 0:
return p.Precpred(p.GetParserRuleContext(), 1)
default:
panic("No predicate with index: " + fmt.Sprint(predIndex))
}
}
func (p *CELParser) Calc_Sempred(localctx antlr.RuleContext, predIndex int) bool {
switch predIndex {
case 1:
return p.Precpred(p.GetParserRuleContext(), 2)
case 2:
return p.Precpred(p.GetParserRuleContext(), 1)
default:
panic("No predicate with index: " + fmt.Sprint(predIndex))
}
}
func (p *CELParser) Member_Sempred(localctx antlr.RuleContext, predIndex int) bool {
switch predIndex {
case 3:
return p.Precpred(p.GetParserRuleContext(), 3)
case 4:
return p.Precpred(p.GetParserRuleContext(), 2)
case 5:
return p.Precpred(p.GetParserRuleContext(), 1)
default:
panic("No predicate with index: " + fmt.Sprint(predIndex))
}
}
// Copyright 2018 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package parser
import (
"sync"
antlr "github.com/antlr4-go/antlr/v4"
"github.com/google/cel-go/common"
"github.com/google/cel-go/common/ast"
"github.com/google/cel-go/common/types"
"github.com/google/cel-go/common/types/ref"
)
type parserHelper struct {
exprFactory ast.ExprFactory
source common.Source
sourceInfo *ast.SourceInfo
nextID int64
}
func newParserHelper(source common.Source, fac ast.ExprFactory) *parserHelper {
return &parserHelper{
exprFactory: fac,
source: source,
sourceInfo: ast.NewSourceInfo(source),
nextID: 1,
}
}
func (p *parserHelper) getSourceInfo() *ast.SourceInfo {
return p.sourceInfo
}
func (p *parserHelper) newLiteral(ctx any, value ref.Val) ast.Expr {
return p.exprFactory.NewLiteral(p.newID(ctx), value)
}
func (p *parserHelper) newLiteralBool(ctx any, value bool) ast.Expr {
return p.newLiteral(ctx, types.Bool(value))
}
func (p *parserHelper) newLiteralString(ctx any, value string) ast.Expr {
return p.newLiteral(ctx, types.String(value))
}
func (p *parserHelper) newLiteralBytes(ctx any, value []byte) ast.Expr {
return p.newLiteral(ctx, types.Bytes(value))
}
func (p *parserHelper) newLiteralInt(ctx any, value int64) ast.Expr {
return p.newLiteral(ctx, types.Int(value))
}
func (p *parserHelper) newLiteralUint(ctx any, value uint64) ast.Expr {
return p.newLiteral(ctx, types.Uint(value))
}
func (p *parserHelper) newLiteralDouble(ctx any, value float64) ast.Expr {
return p.newLiteral(ctx, types.Double(value))
}
func (p *parserHelper) newIdent(ctx any, name string) ast.Expr {
return p.exprFactory.NewIdent(p.newID(ctx), name)
}
func (p *parserHelper) newSelect(ctx any, operand ast.Expr, field string) ast.Expr {
return p.exprFactory.NewSelect(p.newID(ctx), operand, field)
}
func (p *parserHelper) newPresenceTest(ctx any, operand ast.Expr, field string) ast.Expr {
return p.exprFactory.NewPresenceTest(p.newID(ctx), operand, field)
}
func (p *parserHelper) newGlobalCall(ctx any, function string, args ...ast.Expr) ast.Expr {
return p.exprFactory.NewCall(p.newID(ctx), function, args...)
}
func (p *parserHelper) newReceiverCall(ctx any, function string, target ast.Expr, args ...ast.Expr) ast.Expr {
return p.exprFactory.NewMemberCall(p.newID(ctx), function, target, args...)
}
func (p *parserHelper) newList(ctx any, elements []ast.Expr, optionals ...int32) ast.Expr {
return p.exprFactory.NewList(p.newID(ctx), elements, optionals)
}
func (p *parserHelper) newMap(ctx any, entries ...ast.EntryExpr) ast.Expr {
return p.exprFactory.NewMap(p.newID(ctx), entries)
}
func (p *parserHelper) newMapEntry(entryID int64, key ast.Expr, value ast.Expr, optional bool) ast.EntryExpr {
return p.exprFactory.NewMapEntry(entryID, key, value, optional)
}
func (p *parserHelper) newObject(ctx any, typeName string, fields ...ast.EntryExpr) ast.Expr {
return p.exprFactory.NewStruct(p.newID(ctx), typeName, fields)
}
func (p *parserHelper) newObjectField(fieldID int64, field string, value ast.Expr, optional bool) ast.EntryExpr {
return p.exprFactory.NewStructField(fieldID, field, value, optional)
}
func (p *parserHelper) newComprehension(ctx any,
iterRange ast.Expr,
iterVar,
accuVar string,
accuInit ast.Expr,
condition ast.Expr,
step ast.Expr,
result ast.Expr) ast.Expr {
return p.exprFactory.NewComprehension(
p.newID(ctx), iterRange, iterVar, accuVar, accuInit, condition, step, result)
}
func (p *parserHelper) newComprehensionTwoVar(ctx any,
iterRange ast.Expr,
iterVar, iterVar2,
accuVar string,
accuInit ast.Expr,
condition ast.Expr,
step ast.Expr,
result ast.Expr) ast.Expr {
return p.exprFactory.NewComprehensionTwoVar(
p.newID(ctx), iterRange, iterVar, iterVar2, accuVar, accuInit, condition, step, result)
}
func (p *parserHelper) newID(ctx any) int64 {
if id, isID := ctx.(int64); isID {
return id
}
return p.id(ctx)
}
func (p *parserHelper) newExpr(ctx any) ast.Expr {
return p.exprFactory.NewUnspecifiedExpr(p.newID(ctx))
}
func (p *parserHelper) id(ctx any) int64 {
var offset ast.OffsetRange
switch c := ctx.(type) {
case antlr.ParserRuleContext:
start := c.GetStart()
offset.Start = p.sourceInfo.ComputeOffset(int32(start.GetLine()), int32(start.GetColumn()))
offset.Stop = offset.Start + int32(len(c.GetText()))
case antlr.Token:
offset.Start = p.sourceInfo.ComputeOffset(int32(c.GetLine()), int32(c.GetColumn()))
offset.Stop = offset.Start + int32(len(c.GetText()))
case common.Location:
offset.Start = p.sourceInfo.ComputeOffset(int32(c.Line()), int32(c.Column()))
offset.Stop = offset.Start
case ast.OffsetRange:
offset = c
default:
// This should only happen if the ctx is nil
return -1
}
id := p.nextID
p.sourceInfo.SetOffsetRange(id, offset)
p.nextID++
return id
}
func (p *parserHelper) deleteID(id int64) {
p.sourceInfo.ClearOffsetRange(id)
if id == p.nextID-1 {
p.nextID--
}
}
func (p *parserHelper) getLocation(id int64) common.Location {
return p.sourceInfo.GetStartLocation(id)
}
func (p *parserHelper) getLocationByOffset(offset int32) common.Location {
return p.getSourceInfo().GetLocationByOffset(offset)
}
// buildMacroCallArg iterates the expression and returns a new expression
// where all macros have been replaced by their IDs in MacroCalls
func (p *parserHelper) buildMacroCallArg(expr ast.Expr) ast.Expr {
if _, found := p.sourceInfo.GetMacroCall(expr.ID()); found {
return p.exprFactory.NewUnspecifiedExpr(expr.ID())
}
switch expr.Kind() {
case ast.CallKind:
// Iterate the AST from `expr` recursively looking for macros. Because we are at most
// starting from the top level macro, this recursion is bounded by the size of the AST. This
// means that the depth check on the AST during parsing will catch recursion overflows
// before we get to here.
call := expr.AsCall()
macroArgs := make([]ast.Expr, len(call.Args()))
for index, arg := range call.Args() {
macroArgs[index] = p.buildMacroCallArg(arg)
}
if !call.IsMemberFunction() {
return p.exprFactory.NewCall(expr.ID(), call.FunctionName(), macroArgs...)
}
macroTarget := p.buildMacroCallArg(call.Target())
return p.exprFactory.NewMemberCall(expr.ID(), call.FunctionName(), macroTarget, macroArgs...)
case ast.ListKind:
list := expr.AsList()
macroListArgs := make([]ast.Expr, list.Size())
for i, elem := range list.Elements() {
macroListArgs[i] = p.buildMacroCallArg(elem)
}
return p.exprFactory.NewList(expr.ID(), macroListArgs, list.OptionalIndices())
}
return expr
}
// addMacroCall adds the macro the the MacroCalls map in source info. If a macro has args/subargs/target
// that are macros, their ID will be stored instead for later self-lookups.
func (p *parserHelper) addMacroCall(exprID int64, function string, target ast.Expr, args ...ast.Expr) {
macroArgs := make([]ast.Expr, len(args))
for index, arg := range args {
macroArgs[index] = p.buildMacroCallArg(arg)
}
if target == nil {
p.sourceInfo.SetMacroCall(exprID, p.exprFactory.NewCall(0, function, macroArgs...))
return
}
macroTarget := target
if _, found := p.sourceInfo.GetMacroCall(target.ID()); found {
macroTarget = p.exprFactory.NewUnspecifiedExpr(target.ID())
} else {
macroTarget = p.buildMacroCallArg(target)
}
p.sourceInfo.SetMacroCall(exprID, p.exprFactory.NewMemberCall(0, function, macroTarget, macroArgs...))
}
// logicManager compacts logical trees into a more efficient structure which is semantically
// equivalent with how the logic graph is constructed by the ANTLR parser.
//
// The purpose of the logicManager is to ensure a compact serialization format for the logical &&, ||
// operators which have a tendency to create long DAGs which are skewed in one direction. Since the
// operators are commutative re-ordering the terms *must not* affect the evaluation result.
//
// The logic manager will either render the terms to N-chained && / || operators as a single logical
// call with N-terms, or will rebalance the tree. Rebalancing the terms is a safe, if somewhat
// controversial choice as it alters the traditional order of execution assumptions present in most
// expressions.
type logicManager struct {
exprFactory ast.ExprFactory
function string
terms []ast.Expr
ops []int64
variadicASTs bool
}
// newVariadicLogicManager creates a logic manager instance bound to a specific function and its first term.
func newVariadicLogicManager(fac ast.ExprFactory, function string, term ast.Expr) *logicManager {
return &logicManager{
exprFactory: fac,
function: function,
terms: []ast.Expr{term},
ops: []int64{},
variadicASTs: true,
}
}
// newBalancingLogicManager creates a logic manager instance bound to a specific function and its first term.
func newBalancingLogicManager(fac ast.ExprFactory, function string, term ast.Expr) *logicManager {
return &logicManager{
exprFactory: fac,
function: function,
terms: []ast.Expr{term},
ops: []int64{},
variadicASTs: false,
}
}
// addTerm adds an operation identifier and term to the set of terms to be balanced.
func (l *logicManager) addTerm(op int64, term ast.Expr) {
l.terms = append(l.terms, term)
l.ops = append(l.ops, op)
}
// toExpr renders the logic graph into an Expr value, either balancing a tree of logical
// operations or creating a variadic representation of the logical operator.
func (l *logicManager) toExpr() ast.Expr {
if len(l.terms) == 1 {
return l.terms[0]
}
if l.variadicASTs {
return l.exprFactory.NewCall(l.ops[0], l.function, l.terms...)
}
return l.balancedTree(0, len(l.ops)-1)
}
// balancedTree recursively balances the terms provided to a commutative operator.
func (l *logicManager) balancedTree(lo, hi int) ast.Expr {
mid := (lo + hi + 1) / 2
var left ast.Expr
if mid == lo {
left = l.terms[mid]
} else {
left = l.balancedTree(lo, mid-1)
}
var right ast.Expr
if mid == hi {
right = l.terms[mid+1]
} else {
right = l.balancedTree(mid+1, hi)
}
return l.exprFactory.NewCall(l.ops[mid], l.function, left, right)
}
type exprHelper struct {
*parserHelper
id int64
}
func (e *exprHelper) nextMacroID() int64 {
return e.parserHelper.id(e.parserHelper.getLocation(e.id))
}
// Copy implements the ExprHelper interface method by producing a copy of the input Expr value
// with a fresh set of numeric identifiers the Expr and all its descendants.
func (e *exprHelper) Copy(expr ast.Expr) ast.Expr {
offsetRange, _ := e.parserHelper.sourceInfo.GetOffsetRange(expr.ID())
copyID := e.parserHelper.newID(offsetRange)
switch expr.Kind() {
case ast.LiteralKind:
return e.exprFactory.NewLiteral(copyID, expr.AsLiteral())
case ast.IdentKind:
return e.exprFactory.NewIdent(copyID, expr.AsIdent())
case ast.SelectKind:
sel := expr.AsSelect()
op := e.Copy(sel.Operand())
if sel.IsTestOnly() {
return e.exprFactory.NewPresenceTest(copyID, op, sel.FieldName())
}
return e.exprFactory.NewSelect(copyID, op, sel.FieldName())
case ast.CallKind:
call := expr.AsCall()
args := call.Args()
argsCopy := make([]ast.Expr, len(args))
for i, arg := range args {
argsCopy[i] = e.Copy(arg)
}
if !call.IsMemberFunction() {
return e.exprFactory.NewCall(copyID, call.FunctionName(), argsCopy...)
}
return e.exprFactory.NewMemberCall(copyID, call.FunctionName(), e.Copy(call.Target()), argsCopy...)
case ast.ListKind:
list := expr.AsList()
elems := list.Elements()
elemsCopy := make([]ast.Expr, len(elems))
for i, elem := range elems {
elemsCopy[i] = e.Copy(elem)
}
return e.exprFactory.NewList(copyID, elemsCopy, list.OptionalIndices())
case ast.MapKind:
m := expr.AsMap()
entries := m.Entries()
entriesCopy := make([]ast.EntryExpr, len(entries))
for i, en := range entries {
entry := en.AsMapEntry()
entryID := e.nextMacroID()
entriesCopy[i] = e.exprFactory.NewMapEntry(entryID,
e.Copy(entry.Key()), e.Copy(entry.Value()), entry.IsOptional())
}
return e.exprFactory.NewMap(copyID, entriesCopy)
case ast.StructKind:
s := expr.AsStruct()
fields := s.Fields()
fieldsCopy := make([]ast.EntryExpr, len(fields))
for i, f := range fields {
field := f.AsStructField()
fieldID := e.nextMacroID()
fieldsCopy[i] = e.exprFactory.NewStructField(fieldID,
field.Name(), e.Copy(field.Value()), field.IsOptional())
}
return e.exprFactory.NewStruct(copyID, s.TypeName(), fieldsCopy)
case ast.ComprehensionKind:
compre := expr.AsComprehension()
iterRange := e.Copy(compre.IterRange())
accuInit := e.Copy(compre.AccuInit())
cond := e.Copy(compre.LoopCondition())
step := e.Copy(compre.LoopStep())
result := e.Copy(compre.Result())
// All comprehensions can be represented by the two-variable comprehension since the
// differentiation between one and two-variable is whether the iterVar2 value is non-empty.
return e.exprFactory.NewComprehensionTwoVar(copyID,
iterRange, compre.IterVar(), compre.IterVar2(), compre.AccuVar(), accuInit, cond, step, result)
}
return e.exprFactory.NewUnspecifiedExpr(copyID)
}
// NewLiteral implements the ExprHelper interface method.
func (e *exprHelper) NewLiteral(value ref.Val) ast.Expr {
return e.exprFactory.NewLiteral(e.nextMacroID(), value)
}
// NewList implements the ExprHelper interface method.
func (e *exprHelper) NewList(elems ...ast.Expr) ast.Expr {
return e.exprFactory.NewList(e.nextMacroID(), elems, []int32{})
}
// NewMap implements the ExprHelper interface method.
func (e *exprHelper) NewMap(entries ...ast.EntryExpr) ast.Expr {
return e.exprFactory.NewMap(e.nextMacroID(), entries)
}
// NewMapEntry implements the ExprHelper interface method.
func (e *exprHelper) NewMapEntry(key ast.Expr, val ast.Expr, optional bool) ast.EntryExpr {
return e.exprFactory.NewMapEntry(e.nextMacroID(), key, val, optional)
}
// NewStruct implements the ExprHelper interface method.
func (e *exprHelper) NewStruct(typeName string, fieldInits ...ast.EntryExpr) ast.Expr {
return e.exprFactory.NewStruct(e.nextMacroID(), typeName, fieldInits)
}
// NewStructField implements the ExprHelper interface method.
func (e *exprHelper) NewStructField(field string, init ast.Expr, optional bool) ast.EntryExpr {
return e.exprFactory.NewStructField(e.nextMacroID(), field, init, optional)
}
// NewComprehension implements the ExprHelper interface method.
func (e *exprHelper) NewComprehension(
iterRange ast.Expr,
iterVar string,
accuVar string,
accuInit ast.Expr,
condition ast.Expr,
step ast.Expr,
result ast.Expr) ast.Expr {
return e.exprFactory.NewComprehension(
e.nextMacroID(), iterRange, iterVar, accuVar, accuInit, condition, step, result)
}
// NewComprehensionTwoVar implements the ExprHelper interface method.
func (e *exprHelper) NewComprehensionTwoVar(
iterRange ast.Expr,
iterVar,
iterVar2,
accuVar string,
accuInit,
condition,
step,
result ast.Expr) ast.Expr {
return e.exprFactory.NewComprehensionTwoVar(
e.nextMacroID(), iterRange, iterVar, iterVar2, accuVar, accuInit, condition, step, result)
}
// NewIdent implements the ExprHelper interface method.
func (e *exprHelper) NewIdent(name string) ast.Expr {
return e.exprFactory.NewIdent(e.nextMacroID(), name)
}
// NewAccuIdent implements the ExprHelper interface method.
func (e *exprHelper) NewAccuIdent() ast.Expr {
return e.exprFactory.NewAccuIdent(e.nextMacroID())
}
// AccuIdentName implements the ExprHelper interface method.
func (e *exprHelper) AccuIdentName() string {
return e.exprFactory.AccuIdentName()
}
// NewGlobalCall implements the ExprHelper interface method.
func (e *exprHelper) NewCall(function string, args ...ast.Expr) ast.Expr {
return e.exprFactory.NewCall(e.nextMacroID(), function, args...)
}
// NewMemberCall implements the ExprHelper interface method.
func (e *exprHelper) NewMemberCall(function string, target ast.Expr, args ...ast.Expr) ast.Expr {
return e.exprFactory.NewMemberCall(e.nextMacroID(), function, target, args...)
}
// NewPresenceTest implements the ExprHelper interface method.
func (e *exprHelper) NewPresenceTest(operand ast.Expr, field string) ast.Expr {
return e.exprFactory.NewPresenceTest(e.nextMacroID(), operand, field)
}
// NewSelect implements the ExprHelper interface method.
func (e *exprHelper) NewSelect(operand ast.Expr, field string) ast.Expr {
return e.exprFactory.NewSelect(e.nextMacroID(), operand, field)
}
// OffsetLocation implements the ExprHelper interface method.
func (e *exprHelper) OffsetLocation(exprID int64) common.Location {
return e.parserHelper.sourceInfo.GetStartLocation(exprID)
}
// NewError associates an error message with a given expression id, populating the source offset location of the error if possible.
func (e *exprHelper) NewError(exprID int64, message string) *common.Error {
return common.NewError(exprID, message, e.OffsetLocation(exprID))
}
var (
// Thread-safe pool of ExprHelper values to minimize alloc overhead of ExprHelper creations.
exprHelperPool = &sync.Pool{
New: func() any {
return &exprHelper{}
},
}
)
// Copyright 2021 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package parser
import (
antlr "github.com/antlr4-go/antlr/v4"
"github.com/google/cel-go/common/runes"
)
type charStream struct {
buf runes.Buffer
pos int
src string
}
// Consume implements (antlr.CharStream).Consume.
func (c *charStream) Consume() {
if c.pos >= c.buf.Len() {
panic("cannot consume EOF")
}
c.pos++
}
// LA implements (antlr.CharStream).LA.
func (c *charStream) LA(offset int) int {
if offset == 0 {
return 0
}
if offset < 0 {
offset++
}
pos := c.pos + offset - 1
if pos < 0 || pos >= c.buf.Len() {
return antlr.TokenEOF
}
return int(c.buf.Get(pos))
}
// LT mimics (*antlr.InputStream).LT.
func (c *charStream) LT(offset int) int {
return c.LA(offset)
}
// Mark implements (antlr.CharStream).Mark.
func (c *charStream) Mark() int {
return -1
}
// Release implements (antlr.CharStream).Release.
func (c *charStream) Release(marker int) {}
// Index implements (antlr.CharStream).Index.
func (c *charStream) Index() int {
return c.pos
}
// Seek implements (antlr.CharStream).Seek.
func (c *charStream) Seek(index int) {
if index <= c.pos {
c.pos = index
return
}
if index < c.buf.Len() {
c.pos = index
} else {
c.pos = c.buf.Len()
}
}
// Size implements (antlr.CharStream).Size.
func (c *charStream) Size() int {
return c.buf.Len()
}
// GetSourceName implements (antlr.CharStream).GetSourceName.
func (c *charStream) GetSourceName() string {
return c.src
}
// GetText implements (antlr.CharStream).GetText.
func (c *charStream) GetText(start, stop int) string {
if stop >= c.buf.Len() {
stop = c.buf.Len() - 1
}
if start >= c.buf.Len() {
return ""
}
return c.buf.Slice(start, stop+1)
}
// GetTextFromTokens implements (antlr.CharStream).GetTextFromTokens.
func (c *charStream) GetTextFromTokens(start, stop antlr.Token) string {
if start != nil && stop != nil {
return c.GetText(start.GetTokenIndex(), stop.GetTokenIndex())
}
return ""
}
// GetTextFromInterval implements (antlr.CharStream).GetTextFromInterval.
func (c *charStream) GetTextFromInterval(i antlr.Interval) string {
return c.GetText(i.Start, i.Stop)
}
// String mimics (*antlr.InputStream).String.
func (c *charStream) String() string {
return c.buf.Slice(0, c.buf.Len())
}
var _ antlr.CharStream = &charStream{}
func newCharStream(buf runes.Buffer, desc string) antlr.CharStream {
return &charStream{
buf: buf,
src: desc,
}
}
// Copyright 2018 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package parser
import (
"fmt"
"github.com/google/cel-go/common"
"github.com/google/cel-go/common/ast"
"github.com/google/cel-go/common/operators"
"github.com/google/cel-go/common/types"
"github.com/google/cel-go/common/types/ref"
)
// MacroOpt defines a functional option for configuring macro behavior.
type MacroOpt func(*macro) *macro
// MacroDocs configures a list of strings into a multiline description for the macro.
func MacroDocs(docs ...string) MacroOpt {
return func(m *macro) *macro {
m.doc = common.MultilineDescription(docs...)
return m
}
}
// MacroExamples configures a list of examples, either as a string or common.MultilineString,
// into an example set to be provided with the macro Documentation() call.
func MacroExamples(examples ...string) MacroOpt {
return func(m *macro) *macro {
m.examples = examples
return m
}
}
// NewGlobalMacro creates a Macro for a global function with the specified arg count.
func NewGlobalMacro(function string, argCount int, expander MacroExpander, opts ...MacroOpt) Macro {
m := ¯o{
function: function,
argCount: argCount,
expander: expander}
for _, opt := range opts {
m = opt(m)
}
return m
}
// NewReceiverMacro creates a Macro for a receiver function matching the specified arg count.
func NewReceiverMacro(function string, argCount int, expander MacroExpander, opts ...MacroOpt) Macro {
m := ¯o{
function: function,
argCount: argCount,
expander: expander,
receiverStyle: true}
for _, opt := range opts {
m = opt(m)
}
return m
}
// NewGlobalVarArgMacro creates a Macro for a global function with a variable arg count.
func NewGlobalVarArgMacro(function string, expander MacroExpander, opts ...MacroOpt) Macro {
m := ¯o{
function: function,
expander: expander,
varArgStyle: true}
for _, opt := range opts {
m = opt(m)
}
return m
}
// NewReceiverVarArgMacro creates a Macro for a receiver function matching a variable arg count.
func NewReceiverVarArgMacro(function string, expander MacroExpander, opts ...MacroOpt) Macro {
m := ¯o{
function: function,
expander: expander,
receiverStyle: true,
varArgStyle: true}
for _, opt := range opts {
m = opt(m)
}
return m
}
// Macro interface for describing the function signature to match and the MacroExpander to apply.
//
// Note: when a Macro should apply to multiple overloads (based on arg count) of a given function,
// a Macro should be created per arg-count.
type Macro interface {
// Function name to match.
Function() string
// ArgCount for the function call.
//
// When the macro is a var-arg style macro, the return value will be zero, but the MacroKey
// will contain a `*` where the arg count would have been.
ArgCount() int
// IsReceiverStyle returns true if the macro matches a receiver style call.
IsReceiverStyle() bool
// MacroKey returns the macro signatures accepted by this macro.
//
// Format: `<function>:<arg-count>:<is-receiver>`.
//
// When the macros is a var-arg style macro, the `arg-count` value is represented as a `*`.
MacroKey() string
// Expander returns the MacroExpander to apply when the macro key matches the parsed call
// signature.
Expander() MacroExpander
}
// Macro type which declares the function name and arg count expected for the
// macro, as well as a macro expansion function.
type macro struct {
function string
receiverStyle bool
varArgStyle bool
argCount int
expander MacroExpander
doc string
examples []string
}
// Function returns the macro's function name (i.e. the function whose syntax it mimics).
func (m *macro) Function() string {
return m.function
}
// ArgCount returns the number of arguments the macro expects.
func (m *macro) ArgCount() int {
return m.argCount
}
// IsReceiverStyle returns whether the macro is receiver style.
func (m *macro) IsReceiverStyle() bool {
return m.receiverStyle
}
// Expander implements the Macro interface method.
func (m *macro) Expander() MacroExpander {
return m.expander
}
// MacroKey implements the Macro interface method.
func (m *macro) MacroKey() string {
if m.varArgStyle {
return makeVarArgMacroKey(m.function, m.receiverStyle)
}
return makeMacroKey(m.function, m.argCount, m.receiverStyle)
}
// Documentation generates documentation and examples for the macro.
func (m *macro) Documentation() *common.Doc {
examples := make([]*common.Doc, len(m.examples))
for i, ex := range m.examples {
examples[i] = common.NewExampleDoc(ex)
}
return common.NewMacroDoc(m.Function(), m.doc, examples...)
}
func makeMacroKey(name string, args int, receiverStyle bool) string {
return fmt.Sprintf("%s:%d:%v", name, args, receiverStyle)
}
func makeVarArgMacroKey(name string, receiverStyle bool) string {
return fmt.Sprintf("%s:*:%v", name, receiverStyle)
}
// MacroExpander converts a call and its associated arguments into a new CEL abstract syntax tree.
//
// If the MacroExpander determines within the implementation that an expansion is not needed it may return
// a nil Expr value to indicate a non-match. However, if an expansion is to be performed, but the arguments
// are not well-formed, the result of the expansion will be an error.
//
// The MacroExpander accepts as arguments a MacroExprHelper as well as the arguments used in the function call
// and produces as output an Expr ast node.
//
// Note: when the Macro.IsReceiverStyle() method returns true, the target argument will be nil.
type MacroExpander func(eh ExprHelper, target ast.Expr, args []ast.Expr) (ast.Expr, *common.Error)
// ExprHelper assists with the creation of Expr values in a manner which is consistent
// the internal semantics and id generation behaviors of the parser and checker libraries.
type ExprHelper interface {
// Copy the input expression with a brand new set of identifiers.
Copy(ast.Expr) ast.Expr
// Literal creates an Expr value for a scalar literal value.
NewLiteral(value ref.Val) ast.Expr
// NewList creates a list literal instruction with an optional set of elements.
NewList(elems ...ast.Expr) ast.Expr
// NewMap creates a CreateStruct instruction for a map where the map is comprised of the
// optional set of key, value entries.
NewMap(entries ...ast.EntryExpr) ast.Expr
// NewMapEntry creates a Map Entry for the key, value pair.
NewMapEntry(key ast.Expr, val ast.Expr, optional bool) ast.EntryExpr
// NewStruct creates a struct literal expression with an optional set of field initializers.
NewStruct(typeName string, fieldInits ...ast.EntryExpr) ast.Expr
// NewStructField creates a new struct field initializer from the field name and value.
NewStructField(field string, init ast.Expr, optional bool) ast.EntryExpr
// NewComprehension creates a new one-variable comprehension instruction.
//
// - iterRange represents the expression that resolves to a list or map where the elements or
// keys (respectively) will be iterated over.
// - iterVar is the variable name for the list element value, or the map key, depending on the
// range type.
// - accuVar is the accumulation variable name, typically parser.AccumulatorName.
// - accuInit is the initial expression whose value will be set for the accuVar prior to
// folding.
// - condition is the expression to test to determine whether to continue folding.
// - step is the expression to evaluation at the conclusion of a single fold iteration.
// - result is the computation to evaluate at the conclusion of the fold.
//
// The accuVar should not shadow variable names that you would like to reference within the
// environment in the step and condition expressions. Presently, the name __result__ is commonly
// used by built-in macros but this may change in the future.
NewComprehension(iterRange ast.Expr,
iterVar,
accuVar string,
accuInit,
condition,
step,
result ast.Expr) ast.Expr
// NewComprehensionTwoVar creates a new two-variable comprehension instruction.
//
// - iterRange represents the expression that resolves to a list or map where the elements or
// keys (respectively) will be iterated over.
// - iterVar is the iteration variable assigned to the list index or the map key.
// - iterVar2 is the iteration variable assigned to the list element value or the map key value.
// - accuVar is the accumulation variable name, typically parser.AccumulatorName.
// - accuInit is the initial expression whose value will be set for the accuVar prior to
// folding.
// - condition is the expression to test to determine whether to continue folding.
// - step is the expression to evaluation at the conclusion of a single fold iteration.
// - result is the computation to evaluate at the conclusion of the fold.
//
// The accuVar should not shadow variable names that you would like to reference within the
// environment in the step and condition expressions. Presently, the name __result__ is commonly
// used by built-in macros but this may change in the future.
NewComprehensionTwoVar(iterRange ast.Expr,
iterVar,
iterVar2,
accuVar string,
accuInit,
condition,
step,
result ast.Expr) ast.Expr
// NewIdent creates an identifier Expr value.
NewIdent(name string) ast.Expr
// NewAccuIdent returns an accumulator identifier for use with comprehension results.
NewAccuIdent() ast.Expr
// AccuIdentName returns the name of the accumulator identifier.
AccuIdentName() string
// NewCall creates a function call Expr value for a global (free) function.
NewCall(function string, args ...ast.Expr) ast.Expr
// NewMemberCall creates a function call Expr value for a receiver-style function.
NewMemberCall(function string, target ast.Expr, args ...ast.Expr) ast.Expr
// NewPresenceTest creates a Select TestOnly Expr value for modelling has() semantics.
NewPresenceTest(operand ast.Expr, field string) ast.Expr
// NewSelect create a field traversal Expr value.
NewSelect(operand ast.Expr, field string) ast.Expr
// OffsetLocation returns the Location of the expression identifier.
OffsetLocation(exprID int64) common.Location
// NewError associates an error message with a given expression id.
NewError(exprID int64, message string) *common.Error
}
var (
// HasMacro expands "has(m.f)" which tests the presence of a field, avoiding the need to
// specify the field as a string.
HasMacro = NewGlobalMacro(operators.Has, 1, MakeHas,
MacroDocs(
`check a protocol buffer message for the presence of a field, or check a map`,
`for the presence of a string key.`,
`Only map accesses using the select notation are supported.`),
MacroExamples(
common.MultilineDescription(
`// true if the 'address' field exists in the 'user' message`,
`has(user.address)`),
common.MultilineDescription(
`// test whether the 'key_name' is set on the map which defines it`,
`has({'key_name': 'value'}.key_name) // true`),
common.MultilineDescription(
`// test whether the 'id' field is set to a non-default value on the Expr{} message literal`,
`has(Expr{}.id) // false`),
))
// AllMacro expands "range.all(var, predicate)" into a comprehension which ensures that all
// elements in the range satisfy the predicate.
AllMacro = NewReceiverMacro(operators.All, 2, MakeAll,
MacroDocs(`tests whether all elements in the input list or all keys in a map`,
`satisfy the given predicate. The all macro behaves in a manner consistent with`,
`the Logical AND operator including in how it absorbs errors and short-circuits.`),
MacroExamples(
`[1, 2, 3].all(x, x > 0) // true`,
`[1, 2, 0].all(x, x > 0) // false`,
`['apple', 'banana', 'cherry'].all(fruit, fruit.size() > 3) // true`,
`[3.14, 2.71, 1.61].all(num, num < 3.0) // false`,
`{'a': 1, 'b': 2, 'c': 3}.all(key, key != 'b') // false`,
common.MultilineDescription(
`// an empty list or map as the range will result in a trivially true result`,
`[].all(x, x > 0) // true`),
))
// ExistsMacro expands "range.exists(var, predicate)" into a comprehension which ensures that
// some element in the range satisfies the predicate.
ExistsMacro = NewReceiverMacro(operators.Exists, 2, MakeExists,
MacroDocs(`tests whether any value in the list or any key in the map`,
`satisfies the predicate expression. The exists macro behaves in a manner`,
`consistent with the Logical OR operator including in how it absorbs errors and`,
`short-circuits.`),
MacroExamples(
`[1, 2, 3].exists(i, i % 2 != 0) // true`,
`[0, -1, 5].exists(num, num < 0) // true`,
`{'x': 'foo', 'y': 'bar'}.exists(key, key.startsWith('z')) // false`,
common.MultilineDescription(
`// an empty list or map as the range will result in a trivially false result`,
`[].exists(i, i > 0) // false`),
common.MultilineDescription(
`// test whether a key name equalling 'iss' exists in the map and the`,
`// value contains the substring 'cel.dev'`,
`// tokens = {'sub': 'me', 'iss': 'https://issuer.cel.dev'}`,
`tokens.exists(k, k == 'iss' && tokens[k].contains('cel.dev'))`),
))
// ExistsOneMacro expands "range.exists_one(var, predicate)", which is true if for exactly one
// element in range the predicate holds.
// Deprecated: Use ExistsOneMacroNew
ExistsOneMacro = NewReceiverMacro(operators.ExistsOne, 2, MakeExistsOne,
MacroDocs(`tests whether exactly one list element or map key satisfies`,
`the predicate expression. This macro does not short-circuit in order to remain`,
`consistent with logical operators being the only operators which can absorb`,
`errors within CEL.`),
MacroExamples(
`[1, 2, 2].exists_one(i, i < 2) // true`,
`{'a': 'hello', 'aa': 'hellohello'}.exists_one(k, k.startsWith('a')) // false`,
`[1, 2, 3, 4].exists_one(num, num % 2 == 0) // false`,
common.MultilineDescription(
`// ensure exactly one key in the map ends in @acme.co`,
`{'wiley@acme.co': 'coyote', 'aa@milne.co': 'bear'}.exists_one(k, k.endsWith('@acme.co')) // true`),
))
// ExistsOneMacroNew expands "range.existsOne(var, predicate)", which is true if for exactly one
// element in range the predicate holds.
ExistsOneMacroNew = NewReceiverMacro("existsOne", 2, MakeExistsOne,
MacroDocs(
`tests whether exactly one list element or map key satisfies the predicate`,
`expression. This macro does not short-circuit in order to remain consistent`,
`with logical operators being the only operators which can absorb errors`,
`within CEL.`),
MacroExamples(
`[1, 2, 2].existsOne(i, i < 2) // true`,
`{'a': 'hello', 'aa': 'hellohello'}.existsOne(k, k.startsWith('a')) // false`,
`[1, 2, 3, 4].existsOne(num, num % 2 == 0) // false`,
common.MultilineDescription(
`// ensure exactly one key in the map ends in @acme.co`,
`{'wiley@acme.co': 'coyote', 'aa@milne.co': 'bear'}.existsOne(k, k.endsWith('@acme.co')) // true`),
))
// MapMacro expands "range.map(var, function)" into a comprehension which applies the function
// to each element in the range to produce a new list.
MapMacro = NewReceiverMacro(operators.Map, 2, MakeMap,
MacroDocs("the three-argument form of map transforms all elements in the input range."),
MacroExamples(
`[1, 2, 3].map(x, x * 2) // [2, 4, 6]`,
`[5, 10, 15].map(x, x / 5) // [1, 2, 3]`,
`['apple', 'banana'].map(fruit, fruit.upperAscii()) // ['APPLE', 'BANANA']`,
common.MultilineDescription(
`// Combine all map key-value pairs into a list`,
`{'hi': 'you', 'howzit': 'bruv'}.map(k,`,
` k + ":" + {'hi': 'you', 'howzit': 'bruv'}[k]) // ['hi:you', 'howzit:bruv']`),
))
// MapFilterMacro expands "range.map(var, predicate, function)" into a comprehension which
// first filters the elements in the range by the predicate, then applies the transform function
// to produce a new list.
MapFilterMacro = NewReceiverMacro(operators.Map, 3, MakeMap,
MacroDocs(`the four-argument form of the map transforms only elements which satisfy`,
`the predicate which is equivalent to chaining the filter and three-argument`,
`map macros together.`),
MacroExamples(
common.MultilineDescription(
`// multiply only numbers divisible two, by 2`,
`[1, 2, 3, 4].map(num, num % 2 == 0, num * 2) // [4, 8]`),
))
// FilterMacro expands "range.filter(var, predicate)" into a comprehension which filters
// elements in the range, producing a new list from the elements that satisfy the predicate.
FilterMacro = NewReceiverMacro(operators.Filter, 2, MakeFilter,
MacroDocs(`returns a list containing only the elements from the input list`,
`that satisfy the given predicate`),
MacroExamples(
`[1, 2, 3].filter(x, x > 1) // [2, 3]`,
`['cat', 'dog', 'bird', 'fish'].filter(pet, pet.size() == 3) // ['cat', 'dog']`,
`[{'a': 10, 'b': 5, 'c': 20}].map(m, m.filter(key, m[key] > 10)) // [['c']]`,
common.MultilineDescription(
`// filter a list to select only emails with the @cel.dev suffix`,
`['alice@buf.io', 'tristan@cel.dev'].filter(v, v.endsWith('@cel.dev')) // ['tristan@cel.dev']`),
common.MultilineDescription(
`// filter a map into a list, selecting only the values for keys that start with 'http-auth'`,
`{'http-auth-agent': 'secret', 'user-agent': 'mozilla'}.filter(k,`,
` k.startsWith('http-auth')) // ['secret']`),
))
// AllMacros includes the list of all spec-supported macros.
AllMacros = []Macro{
HasMacro,
AllMacro,
ExistsMacro,
ExistsOneMacro,
ExistsOneMacroNew,
MapMacro,
MapFilterMacro,
FilterMacro,
}
// NoMacros list.
NoMacros = []Macro{}
)
// AccumulatorName is the traditional variable name assigned to the fold accumulator variable.
const AccumulatorName = "__result__"
// HiddenAccumulatorName is a proposed update to the default fold accumlator variable.
// @result is not normally accessible from source, preventing accidental or intentional collisions
// in user expressions.
const HiddenAccumulatorName = "@result"
type quantifierKind int
const (
quantifierAll quantifierKind = iota
quantifierExists
quantifierExistsOne
)
// MakeAll expands the input call arguments into a comprehension that returns true if all of the
// elements in the range match the predicate expressions:
// <iterRange>.all(<iterVar>, <predicate>)
func MakeAll(eh ExprHelper, target ast.Expr, args []ast.Expr) (ast.Expr, *common.Error) {
return makeQuantifier(quantifierAll, eh, target, args)
}
// MakeExists expands the input call arguments into a comprehension that returns true if any of the
// elements in the range match the predicate expressions:
// <iterRange>.exists(<iterVar>, <predicate>)
func MakeExists(eh ExprHelper, target ast.Expr, args []ast.Expr) (ast.Expr, *common.Error) {
return makeQuantifier(quantifierExists, eh, target, args)
}
// MakeExistsOne expands the input call arguments into a comprehension that returns true if exactly
// one of the elements in the range match the predicate expressions:
// <iterRange>.exists_one(<iterVar>, <predicate>)
func MakeExistsOne(eh ExprHelper, target ast.Expr, args []ast.Expr) (ast.Expr, *common.Error) {
return makeQuantifier(quantifierExistsOne, eh, target, args)
}
// MakeMap expands the input call arguments into a comprehension that transforms each element in the
// input to produce an output list.
//
// There are two call patterns supported by map:
//
// <iterRange>.map(<iterVar>, <transform>)
// <iterRange>.map(<iterVar>, <predicate>, <transform>)
//
// In the second form only iterVar values which return true when provided to the predicate expression
// are transformed.
func MakeMap(eh ExprHelper, target ast.Expr, args []ast.Expr) (ast.Expr, *common.Error) {
v, found := extractIdent(args[0])
if !found {
return nil, eh.NewError(args[0].ID(), "argument is not an identifier")
}
accu := eh.AccuIdentName()
if v == accu || v == AccumulatorName {
return nil, eh.NewError(args[0].ID(), "iteration variable overwrites accumulator variable")
}
var fn ast.Expr
var filter ast.Expr
if len(args) == 3 {
filter = args[1]
fn = args[2]
} else {
filter = nil
fn = args[1]
}
init := eh.NewList()
condition := eh.NewLiteral(types.True)
step := eh.NewCall(operators.Add, eh.NewAccuIdent(), eh.NewList(fn))
if filter != nil {
step = eh.NewCall(operators.Conditional, filter, step, eh.NewAccuIdent())
}
return eh.NewComprehension(target, v, accu, init, condition, step, eh.NewAccuIdent()), nil
}
// MakeFilter expands the input call arguments into a comprehension which produces a list which contains
// only elements which match the provided predicate expression:
// <iterRange>.filter(<iterVar>, <predicate>)
func MakeFilter(eh ExprHelper, target ast.Expr, args []ast.Expr) (ast.Expr, *common.Error) {
v, found := extractIdent(args[0])
if !found {
return nil, eh.NewError(args[0].ID(), "argument is not an identifier")
}
accu := eh.AccuIdentName()
if v == accu || v == AccumulatorName {
return nil, eh.NewError(args[0].ID(), "iteration variable overwrites accumulator variable")
}
filter := args[1]
init := eh.NewList()
condition := eh.NewLiteral(types.True)
step := eh.NewCall(operators.Add, eh.NewAccuIdent(), eh.NewList(args[0]))
step = eh.NewCall(operators.Conditional, filter, step, eh.NewAccuIdent())
return eh.NewComprehension(target, v, accu, init, condition, step, eh.NewAccuIdent()), nil
}
// MakeHas expands the input call arguments into a presence test, e.g. has(<operand>.field)
func MakeHas(eh ExprHelper, target ast.Expr, args []ast.Expr) (ast.Expr, *common.Error) {
if args[0].Kind() == ast.SelectKind {
s := args[0].AsSelect()
return eh.NewPresenceTest(s.Operand(), s.FieldName()), nil
}
return nil, eh.NewError(args[0].ID(), "invalid argument to has() macro")
}
func makeQuantifier(kind quantifierKind, eh ExprHelper, target ast.Expr, args []ast.Expr) (ast.Expr, *common.Error) {
v, found := extractIdent(args[0])
if !found {
return nil, eh.NewError(args[0].ID(), "argument must be a simple name")
}
accu := eh.AccuIdentName()
if v == accu || v == AccumulatorName {
return nil, eh.NewError(args[0].ID(), "iteration variable overwrites accumulator variable")
}
var init ast.Expr
var condition ast.Expr
var step ast.Expr
var result ast.Expr
switch kind {
case quantifierAll:
init = eh.NewLiteral(types.True)
condition = eh.NewCall(operators.NotStrictlyFalse, eh.NewAccuIdent())
step = eh.NewCall(operators.LogicalAnd, eh.NewAccuIdent(), args[1])
result = eh.NewAccuIdent()
case quantifierExists:
init = eh.NewLiteral(types.False)
condition = eh.NewCall(
operators.NotStrictlyFalse,
eh.NewCall(operators.LogicalNot, eh.NewAccuIdent()))
step = eh.NewCall(operators.LogicalOr, eh.NewAccuIdent(), args[1])
result = eh.NewAccuIdent()
case quantifierExistsOne:
init = eh.NewLiteral(types.Int(0))
condition = eh.NewLiteral(types.True)
step = eh.NewCall(operators.Conditional, args[1],
eh.NewCall(operators.Add, eh.NewAccuIdent(), eh.NewLiteral(types.Int(1))), eh.NewAccuIdent())
result = eh.NewCall(operators.Equals, eh.NewAccuIdent(), eh.NewLiteral(types.Int(1)))
default:
return nil, eh.NewError(args[0].ID(), fmt.Sprintf("unrecognized quantifier '%v'", kind))
}
return eh.NewComprehension(target, v, accu, init, condition, step, result), nil
}
func extractIdent(e ast.Expr) (string, bool) {
switch e.Kind() {
case ast.IdentKind:
return e.AsIdent(), true
}
return "", false
}
// Copyright 2021 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package parser
import "fmt"
type options struct {
maxRecursionDepth int
errorReportingLimit int
errorRecoveryTokenLookaheadLimit int
errorRecoveryLimit int
expressionSizeCodePointLimit int
macros map[string]Macro
populateMacroCalls bool
enableOptionalSyntax bool
enableVariadicOperatorASTs bool
enableIdentEscapeSyntax bool
enableHiddenAccumulatorName bool
}
// Option configures the behavior of the parser.
type Option func(*options) error
// MaxRecursionDepth limits the maximum depth the parser will attempt to parse the expression before giving up.
func MaxRecursionDepth(limit int) Option {
return func(opts *options) error {
if limit < -1 {
return fmt.Errorf("max recursion depth must be greater than or equal to -1: %d", limit)
}
opts.maxRecursionDepth = limit
return nil
}
}
// ErrorRecoveryLookaheadTokenLimit limits the number of lexer tokens that may be considered during error recovery.
//
// Error recovery often involves looking ahead in the input to determine if there's a point at which parsing may
// successfully resume. In some pathological cases, the parser can look through quite a large set of input which
// in turn generates a lot of back-tracking and performance degredation.
//
// The limit must be >= 1, and is recommended to be less than the default of 256.
func ErrorRecoveryLookaheadTokenLimit(limit int) Option {
return func(opts *options) error {
if limit < 1 {
return fmt.Errorf("error recovery lookahead token limit must be at least 1: %d", limit)
}
opts.errorRecoveryTokenLookaheadLimit = limit
return nil
}
}
// ErrorRecoveryLimit limits the number of attempts the parser will perform to recover from an error.
func ErrorRecoveryLimit(limit int) Option {
return func(opts *options) error {
if limit < -1 {
return fmt.Errorf("error recovery limit must be greater than or equal to -1: %d", limit)
}
opts.errorRecoveryLimit = limit
return nil
}
}
// ErrorReportingLimit limits the number of syntax error reports before terminating parsing.
//
// The limit must be at least 1. If unset, the limit will be 100.
func ErrorReportingLimit(limit int) Option {
return func(opts *options) error {
if limit < 1 {
return fmt.Errorf("error reporting limit must be at least 1: %d", limit)
}
opts.errorReportingLimit = limit
return nil
}
}
// ExpressionSizeCodePointLimit is an option which limits the maximum code point count of an
// expression.
func ExpressionSizeCodePointLimit(expressionSizeCodePointLimit int) Option {
return func(opts *options) error {
if expressionSizeCodePointLimit < -1 {
return fmt.Errorf("expression size code point limit must be greater than or equal to -1: %d", expressionSizeCodePointLimit)
}
opts.expressionSizeCodePointLimit = expressionSizeCodePointLimit
return nil
}
}
// Macros adds the given macros to the parser.
func Macros(macros ...Macro) Option {
return func(opts *options) error {
for _, m := range macros {
if m != nil {
if opts.macros == nil {
opts.macros = make(map[string]Macro)
}
opts.macros[m.MacroKey()] = m
}
}
return nil
}
}
// PopulateMacroCalls ensures that the original call signatures replaced by expanded macros
// are preserved in the `SourceInfo` of parse result.
func PopulateMacroCalls(populateMacroCalls bool) Option {
return func(opts *options) error {
opts.populateMacroCalls = populateMacroCalls
return nil
}
}
// EnableOptionalSyntax enables syntax for optional field and index selection.
func EnableOptionalSyntax(optionalSyntax bool) Option {
return func(opts *options) error {
opts.enableOptionalSyntax = optionalSyntax
return nil
}
}
// EnableIdentEscapeSyntax enables backtick (`) escaped field identifiers. This
// supports extended types of characters in identifiers, e.g. foo.`baz-bar`.
func EnableIdentEscapeSyntax(enableIdentEscapeSyntax bool) Option {
return func(opts *options) error {
opts.enableIdentEscapeSyntax = enableIdentEscapeSyntax
return nil
}
}
// EnableHiddenAccumulatorName uses an accumulator variable name that is not a
// normally accessible identifier in source for comprehension macros. Compatibility notes:
// with this option enabled, a parsed AST would be semantically the same as if disabled, but would
// have different internal identifiers in any of the built-in comprehension sub-expressions. When
// disabled, it is possible but almost certainly a logic error to access the accumulator variable.
func EnableHiddenAccumulatorName(enabled bool) Option {
return func(opts *options) error {
opts.enableHiddenAccumulatorName = enabled
return nil
}
}
// EnableVariadicOperatorASTs enables a compact representation of chained like-kind commutative
// operators. e.g. `a || b || c || d` -> `call(op='||', args=[a, b, c, d])`
//
// The benefit of enabling variadic operators ASTs is a more compact representation deeply nested
// logic graphs.
func EnableVariadicOperatorASTs(varArgASTs bool) Option {
return func(opts *options) error {
opts.enableVariadicOperatorASTs = varArgASTs
return nil
}
}
// Copyright 2018 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Package parser declares an expression parser with support for macro
// expansion.
package parser
import (
"errors"
"fmt"
"regexp"
"strconv"
"strings"
antlr "github.com/antlr4-go/antlr/v4"
"github.com/google/cel-go/common"
"github.com/google/cel-go/common/ast"
"github.com/google/cel-go/common/operators"
"github.com/google/cel-go/common/runes"
"github.com/google/cel-go/common/types"
"github.com/google/cel-go/parser/gen"
)
// Parser encapsulates the context necessary to perform parsing for different expressions.
type Parser struct {
options
}
// NewParser builds and returns a new Parser using the provided options.
func NewParser(opts ...Option) (*Parser, error) {
p := &Parser{}
p.enableHiddenAccumulatorName = true
for _, opt := range opts {
if err := opt(&p.options); err != nil {
return nil, err
}
}
if p.errorReportingLimit == 0 {
p.errorReportingLimit = 100
}
if p.maxRecursionDepth == 0 {
p.maxRecursionDepth = 250
}
if p.maxRecursionDepth == -1 {
p.maxRecursionDepth = int((^uint(0)) >> 1)
}
if p.errorRecoveryTokenLookaheadLimit == 0 {
p.errorRecoveryTokenLookaheadLimit = 256
}
if p.errorRecoveryLimit == 0 {
p.errorRecoveryLimit = 30
}
if p.errorRecoveryLimit == -1 {
p.errorRecoveryLimit = int((^uint(0)) >> 1)
}
if p.expressionSizeCodePointLimit == 0 {
p.expressionSizeCodePointLimit = 100_000
}
if p.expressionSizeCodePointLimit == -1 {
p.expressionSizeCodePointLimit = int((^uint(0)) >> 1)
}
// Bool is false by default, so populateMacroCalls will be false by default
return p, nil
}
// mustNewParser does the work of NewParser and panics if an error occurs.
//
// This function is only intended for internal use and is for backwards compatibility in Parse and
// ParseWithMacros, where we know the options will result in an error.
func mustNewParser(opts ...Option) *Parser {
p, err := NewParser(opts...)
if err != nil {
panic(err)
}
return p
}
// Parse parses the expression represented by source and returns the result.
func (p *Parser) Parse(source common.Source) (*ast.AST, *common.Errors) {
errs := common.NewErrors(source)
accu := AccumulatorName
if p.enableHiddenAccumulatorName {
accu = HiddenAccumulatorName
}
fac := ast.NewExprFactoryWithAccumulator(accu)
impl := parser{
errors: &parseErrors{errs},
exprFactory: fac,
helper: newParserHelper(source, fac),
macros: p.macros,
maxRecursionDepth: p.maxRecursionDepth,
errorReportingLimit: p.errorReportingLimit,
errorRecoveryLimit: p.errorRecoveryLimit,
errorRecoveryLookaheadTokenLimit: p.errorRecoveryTokenLookaheadLimit,
populateMacroCalls: p.populateMacroCalls,
enableOptionalSyntax: p.enableOptionalSyntax,
enableVariadicOperatorASTs: p.enableVariadicOperatorASTs,
enableIdentEscapeSyntax: p.enableIdentEscapeSyntax,
}
buf, ok := source.(runes.Buffer)
if !ok {
buf = runes.NewBuffer(source.Content())
}
var out ast.Expr
if buf.Len() > p.expressionSizeCodePointLimit {
out = impl.reportError(common.NoLocation,
"expression code point size exceeds limit: size: %d, limit %d",
buf.Len(), p.expressionSizeCodePointLimit)
} else {
out = impl.parse(buf, source.Description())
}
return ast.NewAST(out, impl.helper.getSourceInfo()), errs
}
// reservedIds are not legal to use as variables. We exclude them post-parse, as they *are* valid
// field names for protos, and it would complicate the grammar to distinguish the cases.
var reservedIds = map[string]struct{}{
"as": {},
"break": {},
"const": {},
"continue": {},
"else": {},
"false": {},
"for": {},
"function": {},
"if": {},
"import": {},
"in": {},
"let": {},
"loop": {},
"package": {},
"namespace": {},
"null": {},
"return": {},
"true": {},
"var": {},
"void": {},
"while": {},
}
func unescapeIdent(in string) (string, error) {
if len(in) <= 2 {
return "", errors.New("invalid escaped identifier: underflow")
}
return in[1 : len(in)-1], nil
}
// normalizeIdent returns the interpreted identifier.
func (p *parser) normalizeIdent(ctx gen.IEscapeIdentContext) (string, error) {
switch ident := ctx.(type) {
case *gen.SimpleIdentifierContext:
return ident.GetId().GetText(), nil
case *gen.EscapedIdentifierContext:
if !p.enableIdentEscapeSyntax {
return "", errors.New("unsupported syntax: '`'")
}
return unescapeIdent(ident.GetId().GetText())
}
return "", errors.New("unsupported ident kind")
}
// Parse converts a source input a parsed expression.
// This function calls ParseWithMacros with AllMacros.
//
// Deprecated: Use NewParser().Parse() instead.
func Parse(source common.Source) (*ast.AST, *common.Errors) {
return mustNewParser(Macros(AllMacros...)).Parse(source)
}
type recursionError struct {
message string
}
// Error implements error.
func (re *recursionError) Error() string {
return re.message
}
var _ error = &recursionError{}
type recursionListener struct {
maxDepth int
ruleTypeDepth map[int]*int
}
func (rl *recursionListener) VisitTerminal(node antlr.TerminalNode) {}
func (rl *recursionListener) VisitErrorNode(node antlr.ErrorNode) {}
func (rl *recursionListener) EnterEveryRule(ctx antlr.ParserRuleContext) {
if ctx == nil {
return
}
ruleIndex := ctx.GetRuleIndex()
depth, found := rl.ruleTypeDepth[ruleIndex]
if !found {
var counter = 1
rl.ruleTypeDepth[ruleIndex] = &counter
depth = &counter
} else {
*depth++
}
if *depth > rl.maxDepth {
panic(&recursionError{
message: fmt.Sprintf("expression recursion limit exceeded: %d", rl.maxDepth),
})
}
}
func (rl *recursionListener) ExitEveryRule(ctx antlr.ParserRuleContext) {
if ctx == nil {
return
}
ruleIndex := ctx.GetRuleIndex()
if depth, found := rl.ruleTypeDepth[ruleIndex]; found && *depth > 0 {
*depth--
}
}
var _ antlr.ParseTreeListener = &recursionListener{}
type tooManyErrors struct {
errorReportingLimit int
}
func (t *tooManyErrors) Error() string {
return fmt.Sprintf("More than %d syntax errors", t.errorReportingLimit)
}
var _ error = &tooManyErrors{}
type recoveryLimitError struct {
message string
}
// Error implements error.
func (rl *recoveryLimitError) Error() string {
return rl.message
}
type lookaheadLimitError struct {
message string
}
func (ll *lookaheadLimitError) Error() string {
return ll.message
}
var _ error = &recoveryLimitError{}
type recoveryLimitErrorStrategy struct {
*antlr.DefaultErrorStrategy
errorRecoveryLimit int
errorRecoveryTokenLookaheadLimit int
recoveryAttempts int
}
type lookaheadConsumer struct {
antlr.Parser
errorRecoveryTokenLookaheadLimit int
lookaheadAttempts int
}
func (lc *lookaheadConsumer) Consume() antlr.Token {
if lc.lookaheadAttempts >= lc.errorRecoveryTokenLookaheadLimit {
panic(&lookaheadLimitError{
message: fmt.Sprintf("error recovery token lookahead limit exceeded: %d", lc.errorRecoveryTokenLookaheadLimit),
})
}
lc.lookaheadAttempts++
return lc.Parser.Consume()
}
func (rl *recoveryLimitErrorStrategy) Recover(recognizer antlr.Parser, e antlr.RecognitionException) {
rl.checkAttempts(recognizer)
lc := &lookaheadConsumer{Parser: recognizer, errorRecoveryTokenLookaheadLimit: rl.errorRecoveryTokenLookaheadLimit}
rl.DefaultErrorStrategy.Recover(lc, e)
}
func (rl *recoveryLimitErrorStrategy) RecoverInline(recognizer antlr.Parser) antlr.Token {
rl.checkAttempts(recognizer)
lc := &lookaheadConsumer{Parser: recognizer, errorRecoveryTokenLookaheadLimit: rl.errorRecoveryTokenLookaheadLimit}
return rl.DefaultErrorStrategy.RecoverInline(lc)
}
func (rl *recoveryLimitErrorStrategy) checkAttempts(recognizer antlr.Parser) {
if rl.recoveryAttempts == rl.errorRecoveryLimit {
rl.recoveryAttempts++
msg := fmt.Sprintf("error recovery attempt limit exceeded: %d", rl.errorRecoveryLimit)
recognizer.NotifyErrorListeners(msg, nil, nil)
panic(&recoveryLimitError{
message: msg,
})
}
rl.recoveryAttempts++
}
var _ antlr.ErrorStrategy = &recoveryLimitErrorStrategy{}
type parser struct {
gen.BaseCELVisitor
errors *parseErrors
exprFactory ast.ExprFactory
helper *parserHelper
macros map[string]Macro
recursionDepth int
errorReports int
maxRecursionDepth int
errorReportingLimit int
errorRecoveryLimit int
errorRecoveryLookaheadTokenLimit int
populateMacroCalls bool
enableOptionalSyntax bool
enableVariadicOperatorASTs bool
enableIdentEscapeSyntax bool
}
var _ gen.CELVisitor = (*parser)(nil)
func (p *parser) parse(expr runes.Buffer, desc string) ast.Expr {
lexer := gen.NewCELLexer(newCharStream(expr, desc))
lexer.RemoveErrorListeners()
lexer.AddErrorListener(p)
prsr := gen.NewCELParser(antlr.NewCommonTokenStream(lexer, 0))
prsr.RemoveErrorListeners()
prsrListener := &recursionListener{
maxDepth: p.maxRecursionDepth,
ruleTypeDepth: map[int]*int{},
}
prsr.AddErrorListener(p)
prsr.AddParseListener(prsrListener)
prsr.SetErrorHandler(&recoveryLimitErrorStrategy{
DefaultErrorStrategy: antlr.NewDefaultErrorStrategy(),
errorRecoveryLimit: p.errorRecoveryLimit,
errorRecoveryTokenLookaheadLimit: p.errorRecoveryLookaheadTokenLimit,
})
defer func() {
if val := recover(); val != nil {
switch err := val.(type) {
case *lookaheadLimitError:
p.errors.internalError(err.Error())
case *recursionError:
p.errors.internalError(err.Error())
case *tooManyErrors:
// do nothing
case *recoveryLimitError:
// do nothing, listeners already notified and error reported.
default:
panic(val)
}
}
}()
return p.Visit(prsr.Start_()).(ast.Expr)
}
// Visitor implementations.
func (p *parser) Visit(tree antlr.ParseTree) any {
t := unnest(tree)
switch tree := t.(type) {
case *gen.StartContext:
return p.VisitStart(tree)
case *gen.ExprContext:
p.checkAndIncrementRecursionDepth()
out := p.VisitExpr(tree)
p.decrementRecursionDepth()
return out
case *gen.ConditionalAndContext:
return p.VisitConditionalAnd(tree)
case *gen.ConditionalOrContext:
return p.VisitConditionalOr(tree)
case *gen.RelationContext:
p.checkAndIncrementRecursionDepth()
out := p.VisitRelation(tree)
p.decrementRecursionDepth()
return out
case *gen.CalcContext:
p.checkAndIncrementRecursionDepth()
out := p.VisitCalc(tree)
p.decrementRecursionDepth()
return out
case *gen.LogicalNotContext:
return p.VisitLogicalNot(tree)
case *gen.IdentContext:
return p.VisitIdent(tree)
case *gen.GlobalCallContext:
return p.VisitGlobalCall(tree)
case *gen.SelectContext:
p.checkAndIncrementRecursionDepth()
out := p.VisitSelect(tree)
p.decrementRecursionDepth()
return out
case *gen.MemberCallContext:
p.checkAndIncrementRecursionDepth()
out := p.VisitMemberCall(tree)
p.decrementRecursionDepth()
return out
case *gen.MapInitializerListContext:
return p.VisitMapInitializerList(tree)
case *gen.NegateContext:
return p.VisitNegate(tree)
case *gen.IndexContext:
p.checkAndIncrementRecursionDepth()
out := p.VisitIndex(tree)
p.decrementRecursionDepth()
return out
case *gen.UnaryContext:
return p.VisitUnary(tree)
case *gen.CreateListContext:
return p.VisitCreateList(tree)
case *gen.CreateMessageContext:
return p.VisitCreateMessage(tree)
case *gen.CreateStructContext:
return p.VisitCreateStruct(tree)
case *gen.IntContext:
return p.VisitInt(tree)
case *gen.UintContext:
return p.VisitUint(tree)
case *gen.DoubleContext:
return p.VisitDouble(tree)
case *gen.StringContext:
return p.VisitString(tree)
case *gen.BytesContext:
return p.VisitBytes(tree)
case *gen.BoolFalseContext:
return p.VisitBoolFalse(tree)
case *gen.BoolTrueContext:
return p.VisitBoolTrue(tree)
case *gen.NullContext:
return p.VisitNull(tree)
}
// Report at least one error if the parser reaches an unknown parse element.
// Typically, this happens if the parser has already encountered a syntax error elsewhere.
if p.errors.errorCount() == 0 {
txt := "<<nil>>"
if t != nil {
txt = fmt.Sprintf("<<%T>>", t)
}
return p.reportError(common.NoLocation, "unknown parse element encountered: %s", txt)
}
return p.helper.newExpr(common.NoLocation)
}
// Visit a parse tree produced by CELParser#start.
func (p *parser) VisitStart(ctx *gen.StartContext) any {
return p.Visit(ctx.Expr())
}
// Visit a parse tree produced by CELParser#expr.
func (p *parser) VisitExpr(ctx *gen.ExprContext) any {
result := p.Visit(ctx.GetE()).(ast.Expr)
if ctx.GetOp() == nil {
return result
}
opID := p.helper.id(ctx.GetOp())
ifTrue := p.Visit(ctx.GetE1()).(ast.Expr)
ifFalse := p.Visit(ctx.GetE2()).(ast.Expr)
return p.globalCallOrMacro(opID, operators.Conditional, result, ifTrue, ifFalse)
}
// Visit a parse tree produced by CELParser#conditionalOr.
func (p *parser) VisitConditionalOr(ctx *gen.ConditionalOrContext) any {
result := p.Visit(ctx.GetE()).(ast.Expr)
l := p.newLogicManager(operators.LogicalOr, result)
rest := ctx.GetE1()
for i, op := range ctx.GetOps() {
if i >= len(rest) {
return p.reportError(ctx, "unexpected character, wanted '||'")
}
next := p.Visit(rest[i]).(ast.Expr)
opID := p.helper.id(op)
l.addTerm(opID, next)
}
return l.toExpr()
}
// Visit a parse tree produced by CELParser#conditionalAnd.
func (p *parser) VisitConditionalAnd(ctx *gen.ConditionalAndContext) any {
result := p.Visit(ctx.GetE()).(ast.Expr)
l := p.newLogicManager(operators.LogicalAnd, result)
rest := ctx.GetE1()
for i, op := range ctx.GetOps() {
if i >= len(rest) {
return p.reportError(ctx, "unexpected character, wanted '&&'")
}
next := p.Visit(rest[i]).(ast.Expr)
opID := p.helper.id(op)
l.addTerm(opID, next)
}
return l.toExpr()
}
// Visit a parse tree produced by CELParser#relation.
func (p *parser) VisitRelation(ctx *gen.RelationContext) any {
opText := ""
if ctx.GetOp() != nil {
opText = ctx.GetOp().GetText()
}
if op, found := operators.Find(opText); found {
lhs := p.Visit(ctx.Relation(0)).(ast.Expr)
opID := p.helper.id(ctx.GetOp())
rhs := p.Visit(ctx.Relation(1)).(ast.Expr)
return p.globalCallOrMacro(opID, op, lhs, rhs)
}
return p.reportError(ctx, "operator not found")
}
// Visit a parse tree produced by CELParser#calc.
func (p *parser) VisitCalc(ctx *gen.CalcContext) any {
opText := ""
if ctx.GetOp() != nil {
opText = ctx.GetOp().GetText()
}
if op, found := operators.Find(opText); found {
lhs := p.Visit(ctx.Calc(0)).(ast.Expr)
opID := p.helper.id(ctx.GetOp())
rhs := p.Visit(ctx.Calc(1)).(ast.Expr)
return p.globalCallOrMacro(opID, op, lhs, rhs)
}
return p.reportError(ctx, "operator not found")
}
func (p *parser) VisitUnary(ctx *gen.UnaryContext) any {
return p.helper.newLiteralString(ctx, "<<error>>")
}
// Visit a parse tree produced by CELParser#LogicalNot.
func (p *parser) VisitLogicalNot(ctx *gen.LogicalNotContext) any {
if len(ctx.GetOps())%2 == 0 {
return p.Visit(ctx.Member())
}
opID := p.helper.id(ctx.GetOps()[0])
target := p.Visit(ctx.Member()).(ast.Expr)
return p.globalCallOrMacro(opID, operators.LogicalNot, target)
}
func (p *parser) VisitNegate(ctx *gen.NegateContext) any {
if len(ctx.GetOps())%2 == 0 {
return p.Visit(ctx.Member())
}
opID := p.helper.id(ctx.GetOps()[0])
target := p.Visit(ctx.Member()).(ast.Expr)
return p.globalCallOrMacro(opID, operators.Negate, target)
}
// VisitSelect visits a parse tree produced by CELParser#Select.
func (p *parser) VisitSelect(ctx *gen.SelectContext) any {
operand := p.Visit(ctx.Member()).(ast.Expr)
// Handle the error case where no valid identifier is specified.
if ctx.GetId() == nil || ctx.GetOp() == nil {
return p.helper.newExpr(ctx)
}
id, err := p.normalizeIdent(ctx.GetId())
if err != nil {
p.reportError(ctx.GetId(), "%v", err)
}
if ctx.GetOpt() != nil {
if !p.enableOptionalSyntax {
return p.reportError(ctx.GetOp(), "unsupported syntax '.?'")
}
return p.helper.newGlobalCall(
ctx.GetOp(),
operators.OptSelect,
operand,
p.helper.newLiteralString(ctx.GetId(), id))
}
return p.helper.newSelect(ctx.GetOp(), operand, id)
}
// VisitMemberCall visits a parse tree produced by CELParser#MemberCall.
func (p *parser) VisitMemberCall(ctx *gen.MemberCallContext) any {
operand := p.Visit(ctx.Member()).(ast.Expr)
// Handle the error case where no valid identifier is specified.
if ctx.GetId() == nil {
return p.helper.newExpr(ctx)
}
id := ctx.GetId().GetText()
opID := p.helper.id(ctx.GetOpen())
return p.receiverCallOrMacro(opID, id, operand, p.visitExprList(ctx.GetArgs())...)
}
// Visit a parse tree produced by CELParser#Index.
func (p *parser) VisitIndex(ctx *gen.IndexContext) any {
target := p.Visit(ctx.Member()).(ast.Expr)
// Handle the error case where no valid identifier is specified.
if ctx.GetOp() == nil {
return p.helper.newExpr(ctx)
}
opID := p.helper.id(ctx.GetOp())
index := p.Visit(ctx.GetIndex()).(ast.Expr)
operator := operators.Index
if ctx.GetOpt() != nil {
if !p.enableOptionalSyntax {
return p.reportError(ctx.GetOp(), "unsupported syntax '[?'")
}
operator = operators.OptIndex
}
return p.globalCallOrMacro(opID, operator, target, index)
}
// Visit a parse tree produced by CELParser#CreateMessage.
func (p *parser) VisitCreateMessage(ctx *gen.CreateMessageContext) any {
messageName := ""
for _, id := range ctx.GetIds() {
if len(messageName) != 0 {
messageName += "."
}
messageName += id.GetText()
}
if ctx.GetLeadingDot() != nil {
messageName = "." + messageName
}
objID := p.helper.id(ctx.GetOp())
entries := p.VisitIFieldInitializerList(ctx.GetEntries()).([]ast.EntryExpr)
return p.helper.newObject(objID, messageName, entries...)
}
// Visit a parse tree of field initializers.
func (p *parser) VisitIFieldInitializerList(ctx gen.IFieldInitializerListContext) any {
if ctx == nil || ctx.GetFields() == nil {
// This is the result of a syntax error handled elswhere, return empty.
return []ast.EntryExpr{}
}
result := make([]ast.EntryExpr, len(ctx.GetFields()))
cols := ctx.GetCols()
vals := ctx.GetValues()
for i, f := range ctx.GetFields() {
if i >= len(cols) || i >= len(vals) {
// This is the result of a syntax error detected elsewhere.
return []ast.EntryExpr{}
}
initID := p.helper.id(cols[i])
optField := f.(*gen.OptFieldContext)
optional := optField.GetOpt() != nil
if !p.enableOptionalSyntax && optional {
p.reportError(optField, "unsupported syntax '?'")
continue
}
// The field may be empty due to a prior error.
fieldName, err := p.normalizeIdent(optField.EscapeIdent())
if err != nil {
p.reportError(ctx, "%v", err)
continue
}
value := p.Visit(vals[i]).(ast.Expr)
field := p.helper.newObjectField(initID, fieldName, value, optional)
result[i] = field
}
return result
}
// Visit a parse tree produced by CELParser#Ident.
func (p *parser) VisitIdent(ctx *gen.IdentContext) any {
identName := ""
if ctx.GetLeadingDot() != nil {
identName = "."
}
// Handle the error case where no valid identifier is specified.
if ctx.GetId() == nil {
return p.helper.newExpr(ctx)
}
// Handle reserved identifiers.
id := ctx.GetId().GetText()
if _, ok := reservedIds[id]; ok {
return p.reportError(ctx, "reserved identifier: %s", id)
}
identName += id
return p.helper.newIdent(ctx.GetId(), identName)
}
// Visit a parse tree produced by CELParser#GlobalCallContext.
func (p *parser) VisitGlobalCall(ctx *gen.GlobalCallContext) any {
identName := ""
if ctx.GetLeadingDot() != nil {
identName = "."
}
// Handle the error case where no valid identifier is specified.
if ctx.GetId() == nil {
return p.helper.newExpr(ctx)
}
// Handle reserved identifiers.
id := ctx.GetId().GetText()
if _, ok := reservedIds[id]; ok {
return p.reportError(ctx, "reserved identifier: %s", id)
}
identName += id
opID := p.helper.id(ctx.GetOp())
return p.globalCallOrMacro(opID, identName, p.visitExprList(ctx.GetArgs())...)
}
// Visit a parse tree produced by CELParser#CreateList.
func (p *parser) VisitCreateList(ctx *gen.CreateListContext) any {
listID := p.helper.id(ctx.GetOp())
elems, optionals := p.visitListInit(ctx.GetElems())
return p.helper.newList(listID, elems, optionals...)
}
// Visit a parse tree produced by CELParser#CreateStruct.
func (p *parser) VisitCreateStruct(ctx *gen.CreateStructContext) any {
structID := p.helper.id(ctx.GetOp())
entries := []ast.EntryExpr{}
if ctx.GetEntries() != nil {
entries = p.Visit(ctx.GetEntries()).([]ast.EntryExpr)
}
return p.helper.newMap(structID, entries...)
}
// Visit a parse tree produced by CELParser#mapInitializerList.
func (p *parser) VisitMapInitializerList(ctx *gen.MapInitializerListContext) any {
if ctx == nil || ctx.GetKeys() == nil {
// This is the result of a syntax error handled elswhere, return empty.
return []ast.EntryExpr{}
}
result := make([]ast.EntryExpr, len(ctx.GetCols()))
keys := ctx.GetKeys()
vals := ctx.GetValues()
for i, col := range ctx.GetCols() {
colID := p.helper.id(col)
if i >= len(keys) || i >= len(vals) {
// This is the result of a syntax error detected elsewhere.
return []ast.EntryExpr{}
}
optKey := keys[i]
optional := optKey.GetOpt() != nil
if !p.enableOptionalSyntax && optional {
p.reportError(optKey, "unsupported syntax '?'")
continue
}
key := p.Visit(optKey.GetE()).(ast.Expr)
value := p.Visit(vals[i]).(ast.Expr)
entry := p.helper.newMapEntry(colID, key, value, optional)
result[i] = entry
}
return result
}
// Visit a parse tree produced by CELParser#Int.
func (p *parser) VisitInt(ctx *gen.IntContext) any {
text := ctx.GetTok().GetText()
base := 10
if strings.HasPrefix(text, "0x") {
base = 16
text = text[2:]
}
if ctx.GetSign() != nil {
text = ctx.GetSign().GetText() + text
}
i, err := strconv.ParseInt(text, base, 64)
if err != nil {
return p.reportError(ctx, "invalid int literal")
}
return p.helper.newLiteralInt(ctx, i)
}
// Visit a parse tree produced by CELParser#Uint.
func (p *parser) VisitUint(ctx *gen.UintContext) any {
text := ctx.GetTok().GetText()
// trim the 'u' designator included in the uint literal.
text = text[:len(text)-1]
base := 10
if strings.HasPrefix(text, "0x") {
base = 16
text = text[2:]
}
i, err := strconv.ParseUint(text, base, 64)
if err != nil {
return p.reportError(ctx, "invalid uint literal")
}
return p.helper.newLiteralUint(ctx, i)
}
// Visit a parse tree produced by CELParser#Double.
func (p *parser) VisitDouble(ctx *gen.DoubleContext) any {
txt := ctx.GetTok().GetText()
if ctx.GetSign() != nil {
txt = ctx.GetSign().GetText() + txt
}
f, err := strconv.ParseFloat(txt, 64)
if err != nil {
return p.reportError(ctx, "invalid double literal")
}
return p.helper.newLiteralDouble(ctx, f)
}
// Visit a parse tree produced by CELParser#String.
func (p *parser) VisitString(ctx *gen.StringContext) any {
s := p.unquote(ctx, ctx.GetTok().GetText(), false)
return p.helper.newLiteralString(ctx, s)
}
// Visit a parse tree produced by CELParser#Bytes.
func (p *parser) VisitBytes(ctx *gen.BytesContext) any {
b := []byte(p.unquote(ctx, ctx.GetTok().GetText()[1:], true))
return p.helper.newLiteralBytes(ctx, b)
}
// Visit a parse tree produced by CELParser#BoolTrue.
func (p *parser) VisitBoolTrue(ctx *gen.BoolTrueContext) any {
return p.helper.newLiteralBool(ctx, true)
}
// Visit a parse tree produced by CELParser#BoolFalse.
func (p *parser) VisitBoolFalse(ctx *gen.BoolFalseContext) any {
return p.helper.newLiteralBool(ctx, false)
}
// Visit a parse tree produced by CELParser#Null.
func (p *parser) VisitNull(ctx *gen.NullContext) any {
return p.helper.exprFactory.NewLiteral(p.helper.newID(ctx), types.NullValue)
}
func (p *parser) visitExprList(ctx gen.IExprListContext) []ast.Expr {
if ctx == nil {
return []ast.Expr{}
}
return p.visitSlice(ctx.GetE())
}
func (p *parser) visitListInit(ctx gen.IListInitContext) ([]ast.Expr, []int32) {
if ctx == nil {
return []ast.Expr{}, []int32{}
}
elements := ctx.GetElems()
result := make([]ast.Expr, len(elements))
optionals := []int32{}
for i, e := range elements {
ex := p.Visit(e.GetE()).(ast.Expr)
if ex == nil {
return []ast.Expr{}, []int32{}
}
result[i] = ex
if e.GetOpt() != nil {
if !p.enableOptionalSyntax {
p.reportError(e.GetOpt(), "unsupported syntax '?'")
continue
}
optionals = append(optionals, int32(i))
}
}
return result, optionals
}
func (p *parser) visitSlice(expressions []gen.IExprContext) []ast.Expr {
if expressions == nil {
return []ast.Expr{}
}
result := make([]ast.Expr, len(expressions))
for i, e := range expressions {
ex := p.Visit(e).(ast.Expr)
result[i] = ex
}
return result
}
func (p *parser) unquote(ctx any, value string, isBytes bool) string {
text, err := unescape(value, isBytes)
if err != nil {
p.reportError(ctx, "%s", err.Error())
return value
}
return text
}
func (p *parser) newLogicManager(function string, term ast.Expr) *logicManager {
if p.enableVariadicOperatorASTs {
return newVariadicLogicManager(p.exprFactory, function, term)
}
return newBalancingLogicManager(p.exprFactory, function, term)
}
func (p *parser) reportError(ctx any, format string, args ...any) ast.Expr {
var location common.Location
err := p.helper.newExpr(ctx)
switch c := ctx.(type) {
case common.Location:
location = c
case antlr.Token, antlr.ParserRuleContext:
location = p.helper.getLocation(err.ID())
}
// Provide arguments to the report error.
p.errors.reportErrorAtID(err.ID(), location, format, args...)
return err
}
// ANTLR Parse listener implementations
func (p *parser) SyntaxError(recognizer antlr.Recognizer, offendingSymbol any, line, column int, msg string, e antlr.RecognitionException) {
offset := p.helper.sourceInfo.ComputeOffset(int32(line), int32(column))
l := p.helper.getLocationByOffset(offset)
// Hack to keep existing error messages consistent with previous versions of CEL when a reserved word
// is used as an identifier. This behavior needs to be overhauled to provide consistent, normalized error
// messages out of ANTLR to prevent future breaking changes related to error message content.
if strings.Contains(msg, "no viable alternative") {
msg = reservedIdentifier.ReplaceAllString(msg, mismatchedReservedIdentifier)
}
// Ensure that no more than 100 syntax errors are reported as this will halt attempts to recover from a
// seriously broken expression.
if p.errorReports < p.errorReportingLimit {
p.errorReports++
p.errors.syntaxError(l, msg)
} else {
tme := &tooManyErrors{errorReportingLimit: p.errorReportingLimit}
p.errors.syntaxError(l, tme.Error())
panic(tme)
}
}
func (p *parser) ReportAmbiguity(recognizer antlr.Parser, dfa *antlr.DFA, startIndex, stopIndex int, exact bool, ambigAlts *antlr.BitSet, configs *antlr.ATNConfigSet) {
// Intentional
}
func (p *parser) ReportAttemptingFullContext(recognizer antlr.Parser, dfa *antlr.DFA, startIndex, stopIndex int, conflictingAlts *antlr.BitSet, configs *antlr.ATNConfigSet) {
// Intentional
}
func (p *parser) ReportContextSensitivity(recognizer antlr.Parser, dfa *antlr.DFA, startIndex, stopIndex, prediction int, configs *antlr.ATNConfigSet) {
// Intentional
}
func (p *parser) globalCallOrMacro(exprID int64, function string, args ...ast.Expr) ast.Expr {
if expr, found := p.expandMacro(exprID, function, nil, args...); found {
return expr
}
return p.helper.newGlobalCall(exprID, function, args...)
}
func (p *parser) receiverCallOrMacro(exprID int64, function string, target ast.Expr, args ...ast.Expr) ast.Expr {
if expr, found := p.expandMacro(exprID, function, target, args...); found {
return expr
}
return p.helper.newReceiverCall(exprID, function, target, args...)
}
func (p *parser) expandMacro(exprID int64, function string, target ast.Expr, args ...ast.Expr) (ast.Expr, bool) {
macro, found := p.macros[makeMacroKey(function, len(args), target != nil)]
if !found {
macro, found = p.macros[makeVarArgMacroKey(function, target != nil)]
if !found {
return nil, false
}
}
eh := exprHelperPool.Get().(*exprHelper)
defer exprHelperPool.Put(eh)
eh.parserHelper = p.helper
eh.id = exprID
expr, err := macro.Expander()(eh, target, args)
// An error indicates that the macro was matched, but the arguments were not well-formed.
if err != nil {
loc := err.Location
if loc == nil {
loc = p.helper.getLocation(exprID)
}
p.helper.deleteID(exprID)
return p.reportError(loc, "%s", err.Message), true
}
// A nil value from the macro indicates that the macro implementation decided that
// an expansion should not be performed.
if expr == nil {
return nil, false
}
if p.populateMacroCalls {
p.helper.addMacroCall(expr.ID(), function, target, args...)
}
p.helper.deleteID(exprID)
return expr, true
}
func (p *parser) checkAndIncrementRecursionDepth() {
p.recursionDepth++
if p.recursionDepth > p.maxRecursionDepth {
panic(&recursionError{message: "max recursion depth exceeded"})
}
}
func (p *parser) decrementRecursionDepth() {
p.recursionDepth--
}
// unnest traverses down the left-hand side of the parse graph until it encounters the first compound
// parse node or the first leaf in the parse graph.
func unnest(tree antlr.ParseTree) antlr.ParseTree {
for tree != nil {
switch t := tree.(type) {
case *gen.ExprContext:
// conditionalOr op='?' conditionalOr : expr
if t.GetOp() != nil {
return t
}
// conditionalOr
tree = t.GetE()
case *gen.ConditionalOrContext:
// conditionalAnd (ops=|| conditionalAnd)*
if t.GetOps() != nil && len(t.GetOps()) > 0 {
return t
}
// conditionalAnd
tree = t.GetE()
case *gen.ConditionalAndContext:
// relation (ops=&& relation)*
if t.GetOps() != nil && len(t.GetOps()) > 0 {
return t
}
// relation
tree = t.GetE()
case *gen.RelationContext:
// relation op relation
if t.GetOp() != nil {
return t
}
// calc
tree = t.Calc()
case *gen.CalcContext:
// calc op calc
if t.GetOp() != nil {
return t
}
// unary
tree = t.Unary()
case *gen.MemberExprContext:
// member expands to one of: primary, select, index, or create message
tree = t.Member()
case *gen.PrimaryExprContext:
// primary expands to one of identifier, nested, create list, create struct, literal
tree = t.Primary()
case *gen.NestedContext:
// contains a nested 'expr'
tree = t.GetE()
case *gen.ConstantLiteralContext:
// expands to a primitive literal
tree = t.Literal()
default:
return t
}
}
return tree
}
var (
reservedIdentifier = regexp.MustCompile("no viable alternative at input '.(true|false|null)'")
mismatchedReservedIdentifier = "mismatched input '$1' expecting IDENTIFIER"
)
// Copyright 2018 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package parser
import (
"errors"
"strings"
"unicode/utf8"
)
// Unescape takes a quoted string, unquotes, and unescapes it.
//
// This function performs escaping compatible with GoogleSQL.
func unescape(value string, isBytes bool) (string, error) {
// All strings normalize newlines to the \n representation.
value = newlineNormalizer.Replace(value)
n := len(value)
// Nothing to unescape / decode.
if n < 2 {
return value, errors.New("unable to unescape string")
}
// Raw string preceded by the 'r|R' prefix.
isRawLiteral := false
if value[0] == 'r' || value[0] == 'R' {
value = value[1:]
n = len(value)
isRawLiteral = true
}
// Quoted string of some form, must have same first and last char.
if value[0] != value[n-1] || (value[0] != '"' && value[0] != '\'') {
return value, errors.New("unable to unescape string")
}
// Normalize the multi-line CEL string representation to a standard
// Go quoted string.
if n >= 6 {
if strings.HasPrefix(value, "'''") {
if !strings.HasSuffix(value, "'''") {
return value, errors.New("unable to unescape string")
}
value = "\"" + value[3:n-3] + "\""
} else if strings.HasPrefix(value, `"""`) {
if !strings.HasSuffix(value, `"""`) {
return value, errors.New("unable to unescape string")
}
value = "\"" + value[3:n-3] + "\""
}
n = len(value)
}
value = value[1 : n-1]
// If there is nothing to escape, then return.
if isRawLiteral || !strings.ContainsRune(value, '\\') {
return value, nil
}
// Otherwise the string contains escape characters.
// The following logic is adapted from `strconv/quote.go`
var runeTmp [utf8.UTFMax]byte
buf := make([]byte, 0, 3*n/2)
for len(value) > 0 {
c, encode, rest, err := unescapeChar(value, isBytes)
if err != nil {
return "", err
}
value = rest
if c < utf8.RuneSelf || !encode {
buf = append(buf, byte(c))
} else {
n := utf8.EncodeRune(runeTmp[:], c)
buf = append(buf, runeTmp[:n]...)
}
}
return string(buf), nil
}
// unescapeChar takes a string input and returns the following info:
//
// value - the escaped unicode rune at the front of the string.
// encode - the value should be unicode-encoded
// tail - the remainder of the input string.
// err - error value, if the character could not be unescaped.
//
// When encode is true the return value may still fit within a single byte,
// but unicode encoding is attempted which is more expensive than when the
// value is known to self-represent as a single byte.
//
// If isBytes is set, unescape as a bytes literal so octal and hex escapes
// represent byte values, not unicode code points.
func unescapeChar(s string, isBytes bool) (value rune, encode bool, tail string, err error) {
// 1. Character is not an escape sequence.
switch c := s[0]; {
case c >= utf8.RuneSelf:
r, size := utf8.DecodeRuneInString(s)
return r, true, s[size:], nil
case c != '\\':
return rune(s[0]), false, s[1:], nil
}
// 2. Last character is the start of an escape sequence.
if len(s) <= 1 {
err = errors.New("unable to unescape string, found '\\' as last character")
return
}
c := s[1]
s = s[2:]
// 3. Common escape sequences shared with Google SQL
switch c {
case 'a':
value = '\a'
case 'b':
value = '\b'
case 'f':
value = '\f'
case 'n':
value = '\n'
case 'r':
value = '\r'
case 't':
value = '\t'
case 'v':
value = '\v'
case '\\':
value = '\\'
case '\'':
value = '\''
case '"':
value = '"'
case '`':
value = '`'
case '?':
value = '?'
// 4. Unicode escape sequences, reproduced from `strconv/quote.go`
case 'x', 'X', 'u', 'U':
n := 0
encode = true
switch c {
case 'x', 'X':
n = 2
encode = !isBytes
case 'u':
n = 4
if isBytes {
err = errors.New("unable to unescape string")
return
}
case 'U':
n = 8
if isBytes {
err = errors.New("unable to unescape string")
return
}
}
var v rune
if len(s) < n {
err = errors.New("unable to unescape string")
return
}
for j := 0; j < n; j++ {
x, ok := unhex(s[j])
if !ok {
err = errors.New("unable to unescape string")
return
}
v = v<<4 | x
}
s = s[n:]
if !isBytes && !utf8.ValidRune(v) {
err = errors.New("invalid unicode code point")
return
}
value = v
// 5. Octal escape sequences, must be three digits \[0-3][0-7][0-7]
case '0', '1', '2', '3':
if len(s) < 2 {
err = errors.New("unable to unescape octal sequence in string")
return
}
v := rune(c - '0')
for j := 0; j < 2; j++ {
x := s[j]
if x < '0' || x > '7' {
err = errors.New("unable to unescape octal sequence in string")
return
}
v = v*8 + rune(x-'0')
}
if !isBytes && !utf8.ValidRune(v) {
err = errors.New("invalid unicode code point")
return
}
value = v
s = s[2:]
encode = !isBytes
// Unknown escape sequence.
default:
err = errors.New("unable to unescape string")
}
tail = s
return
}
func unhex(b byte) (rune, bool) {
c := rune(b)
switch {
case '0' <= c && c <= '9':
return c - '0', true
case 'a' <= c && c <= 'f':
return c - 'a' + 10, true
case 'A' <= c && c <= 'F':
return c - 'A' + 10, true
}
return 0, false
}
var (
newlineNormalizer = strings.NewReplacer("\r\n", "\n", "\r", "\n")
)
// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package parser
import (
"errors"
"fmt"
"regexp"
"strconv"
"strings"
"github.com/google/cel-go/common/ast"
"github.com/google/cel-go/common/operators"
"github.com/google/cel-go/common/types"
"github.com/google/cel-go/common/types/ref"
)
// Unparse takes an input expression and source position information and generates a human-readable
// expression.
//
// Note, unparsing an AST will often generate the same expression as was originally parsed, but some
// formatting may be lost in translation, notably:
//
// - All quoted literals are doubled quoted.
// - Byte literals are represented as octal escapes (same as Google SQL).
// - Floating point values are converted to the small number of digits needed to represent the value.
// - Spacing around punctuation marks may be lost.
// - Parentheses will only be applied when they affect operator precedence.
//
// This function optionally takes in one or more UnparserOption to alter the unparsing behavior, such as
// performing word wrapping on expressions.
func Unparse(expr ast.Expr, info *ast.SourceInfo, opts ...UnparserOption) (string, error) {
unparserOpts := &unparserOption{
wrapOnColumn: defaultWrapOnColumn,
wrapAfterColumnLimit: defaultWrapAfterColumnLimit,
operatorsToWrapOn: defaultOperatorsToWrapOn,
}
var err error
for _, opt := range opts {
unparserOpts, err = opt(unparserOpts)
if err != nil {
return "", err
}
}
un := &unparser{
info: info,
options: unparserOpts,
}
err = un.visit(expr)
if err != nil {
return "", err
}
return un.str.String(), nil
}
var identifierPartPattern *regexp.Regexp = regexp.MustCompile(`^[A-Za-z_][0-9A-Za-z_]*$`)
func maybeQuoteField(field string) string {
if !identifierPartPattern.MatchString(field) || field == "in" {
return "`" + field + "`"
}
return field
}
// unparser visits an expression to reconstruct a human-readable string from an AST.
type unparser struct {
str strings.Builder
info *ast.SourceInfo
options *unparserOption
lastWrappedIndex int
}
func (un *unparser) visit(expr ast.Expr) error {
if expr == nil {
return errors.New("unsupported expression")
}
visited, err := un.visitMaybeMacroCall(expr)
if visited || err != nil {
return err
}
switch expr.Kind() {
case ast.CallKind:
return un.visitCall(expr)
case ast.LiteralKind:
return un.visitConst(expr)
case ast.IdentKind:
return un.visitIdent(expr)
case ast.ListKind:
return un.visitList(expr)
case ast.MapKind:
return un.visitStructMap(expr)
case ast.SelectKind:
return un.visitSelect(expr)
case ast.StructKind:
return un.visitStructMsg(expr)
default:
return fmt.Errorf("unsupported expression: %v", expr)
}
}
func (un *unparser) visitCall(expr ast.Expr) error {
c := expr.AsCall()
fun := c.FunctionName()
switch fun {
// ternary operator
case operators.Conditional:
return un.visitCallConditional(expr)
// optional select operator
case operators.OptSelect:
return un.visitOptSelect(expr)
// index operator
case operators.Index:
return un.visitCallIndex(expr)
// optional index operator
case operators.OptIndex:
return un.visitCallOptIndex(expr)
// unary operators
case operators.LogicalNot, operators.Negate:
return un.visitCallUnary(expr)
// binary operators
case operators.Add,
operators.Divide,
operators.Equals,
operators.Greater,
operators.GreaterEquals,
operators.In,
operators.Less,
operators.LessEquals,
operators.LogicalAnd,
operators.LogicalOr,
operators.Modulo,
operators.Multiply,
operators.NotEquals,
operators.OldIn,
operators.Subtract:
return un.visitCallBinary(expr)
// standard function calls.
default:
return un.visitCallFunc(expr)
}
}
func (un *unparser) visitCallBinary(expr ast.Expr) error {
c := expr.AsCall()
fun := c.FunctionName()
args := c.Args()
lhs := args[0]
// add parens if the current operator is lower precedence than the lhs expr operator.
lhsParen := isComplexOperatorWithRespectTo(fun, lhs)
rhs := args[1]
// add parens if the current operator is lower precedence than the rhs expr operator,
// or the same precedence and the operator is left recursive.
rhsParen := isComplexOperatorWithRespectTo(fun, rhs)
if !rhsParen && isLeftRecursive(fun) {
rhsParen = isSamePrecedence(fun, rhs)
}
err := un.visitMaybeNested(lhs, lhsParen)
if err != nil {
return err
}
unmangled, found := operators.FindReverseBinaryOperator(fun)
if !found {
return fmt.Errorf("cannot unmangle operator: %s", fun)
}
un.writeOperatorWithWrapping(fun, unmangled)
return un.visitMaybeNested(rhs, rhsParen)
}
func (un *unparser) visitCallConditional(expr ast.Expr) error {
c := expr.AsCall()
args := c.Args()
// add parens if operand is a conditional itself.
nested := isSamePrecedence(operators.Conditional, args[0]) ||
isComplexOperator(args[0])
err := un.visitMaybeNested(args[0], nested)
if err != nil {
return err
}
un.writeOperatorWithWrapping(operators.Conditional, "?")
// add parens if operand is a conditional itself.
nested = isSamePrecedence(operators.Conditional, args[1]) ||
isComplexOperator(args[1])
err = un.visitMaybeNested(args[1], nested)
if err != nil {
return err
}
un.str.WriteString(" : ")
// add parens if operand is a conditional itself.
nested = isSamePrecedence(operators.Conditional, args[2]) ||
isComplexOperator(args[2])
return un.visitMaybeNested(args[2], nested)
}
func (un *unparser) visitCallFunc(expr ast.Expr) error {
c := expr.AsCall()
fun := c.FunctionName()
args := c.Args()
if c.IsMemberFunction() {
nested := isBinaryOrTernaryOperator(c.Target())
err := un.visitMaybeNested(c.Target(), nested)
if err != nil {
return err
}
un.str.WriteString(".")
}
un.str.WriteString(fun)
un.str.WriteString("(")
for i, arg := range args {
err := un.visit(arg)
if err != nil {
return err
}
if i < len(args)-1 {
un.str.WriteString(", ")
}
}
un.str.WriteString(")")
return nil
}
func (un *unparser) visitCallIndex(expr ast.Expr) error {
return un.visitCallIndexInternal(expr, "[")
}
func (un *unparser) visitCallOptIndex(expr ast.Expr) error {
return un.visitCallIndexInternal(expr, "[?")
}
func (un *unparser) visitCallIndexInternal(expr ast.Expr, op string) error {
c := expr.AsCall()
args := c.Args()
nested := isBinaryOrTernaryOperator(args[0])
err := un.visitMaybeNested(args[0], nested)
if err != nil {
return err
}
un.str.WriteString(op)
err = un.visit(args[1])
if err != nil {
return err
}
un.str.WriteString("]")
return nil
}
func (un *unparser) visitCallUnary(expr ast.Expr) error {
c := expr.AsCall()
fun := c.FunctionName()
args := c.Args()
unmangled, found := operators.FindReverse(fun)
if !found {
return fmt.Errorf("cannot unmangle operator: %s", fun)
}
un.str.WriteString(unmangled)
nested := isComplexOperator(args[0])
return un.visitMaybeNested(args[0], nested)
}
func (un *unparser) visitConstVal(val ref.Val) error {
optional := false
if optVal, ok := val.(*types.Optional); ok {
if !optVal.HasValue() {
un.str.WriteString("optional.none()")
return nil
}
optional = true
un.str.WriteString("optional.of(")
val = optVal.GetValue()
}
switch val := val.(type) {
case types.Bool:
un.str.WriteString(strconv.FormatBool(bool(val)))
case types.Bytes:
// bytes constants are surrounded with b"<bytes>"
un.str.WriteString(`b"`)
un.str.WriteString(bytesToOctets([]byte(val)))
un.str.WriteString(`"`)
case types.Double:
// represent the float using the minimum required digits
d := strconv.FormatFloat(float64(val), 'g', -1, 64)
un.str.WriteString(d)
if !strings.Contains(d, ".") {
un.str.WriteString(".0")
}
case types.Int:
i := strconv.FormatInt(int64(val), 10)
un.str.WriteString(i)
case types.Null:
un.str.WriteString("null")
case types.String:
// strings will be double quoted with quotes escaped.
un.str.WriteString(strconv.Quote(string(val)))
case types.Uint:
// uint literals have a 'u' suffix.
ui := strconv.FormatUint(uint64(val), 10)
un.str.WriteString(ui)
un.str.WriteString("u")
case *types.Optional:
if err := un.visitConstVal(val); err != nil {
return err
}
default:
return errors.New("unsupported constant")
}
if optional {
un.str.WriteString(")")
}
return nil
}
func (un *unparser) visitConst(expr ast.Expr) error {
val := expr.AsLiteral()
if err := un.visitConstVal(val); err != nil {
return fmt.Errorf("unsupported constant: %v", expr)
}
return nil
}
func (un *unparser) visitIdent(expr ast.Expr) error {
un.str.WriteString(expr.AsIdent())
return nil
}
func (un *unparser) visitList(expr ast.Expr) error {
l := expr.AsList()
elems := l.Elements()
optIndices := make(map[int]bool, len(elems))
for _, idx := range l.OptionalIndices() {
optIndices[int(idx)] = true
}
un.str.WriteString("[")
for i, elem := range elems {
if optIndices[i] {
un.str.WriteString("?")
}
err := un.visit(elem)
if err != nil {
return err
}
if i < len(elems)-1 {
un.str.WriteString(", ")
}
}
un.str.WriteString("]")
return nil
}
func (un *unparser) visitOptSelect(expr ast.Expr) error {
c := expr.AsCall()
args := c.Args()
operand := args[0]
field := args[1].AsLiteral().(types.String)
return un.visitSelectInternal(operand, false, ".?", string(field))
}
func (un *unparser) visitSelect(expr ast.Expr) error {
sel := expr.AsSelect()
return un.visitSelectInternal(sel.Operand(), sel.IsTestOnly(), ".", sel.FieldName())
}
func (un *unparser) visitSelectInternal(operand ast.Expr, testOnly bool, op string, field string) error {
// handle the case when the select expression was generated by the has() macro.
if testOnly {
un.str.WriteString("has(")
}
nested := !testOnly && isBinaryOrTernaryOperator(operand)
err := un.visitMaybeNested(operand, nested)
if err != nil {
return err
}
un.str.WriteString(op)
un.str.WriteString(maybeQuoteField(field))
if testOnly {
un.str.WriteString(")")
}
return nil
}
func (un *unparser) visitStructMsg(expr ast.Expr) error {
m := expr.AsStruct()
fields := m.Fields()
un.str.WriteString(m.TypeName())
un.str.WriteString("{")
for i, f := range fields {
field := f.AsStructField()
f := field.Name()
if field.IsOptional() {
un.str.WriteString("?")
}
un.str.WriteString(maybeQuoteField(f))
un.str.WriteString(": ")
v := field.Value()
err := un.visit(v)
if err != nil {
return err
}
if i < len(fields)-1 {
un.str.WriteString(", ")
}
}
un.str.WriteString("}")
return nil
}
func (un *unparser) visitStructMap(expr ast.Expr) error {
m := expr.AsMap()
entries := m.Entries()
un.str.WriteString("{")
for i, e := range entries {
entry := e.AsMapEntry()
k := entry.Key()
if entry.IsOptional() {
un.str.WriteString("?")
}
err := un.visit(k)
if err != nil {
return err
}
un.str.WriteString(": ")
v := entry.Value()
err = un.visit(v)
if err != nil {
return err
}
if i < len(entries)-1 {
un.str.WriteString(", ")
}
}
un.str.WriteString("}")
return nil
}
func (un *unparser) visitMaybeMacroCall(expr ast.Expr) (bool, error) {
call, found := un.info.GetMacroCall(expr.ID())
if !found {
return false, nil
}
return true, un.visit(call)
}
func (un *unparser) visitMaybeNested(expr ast.Expr, nested bool) error {
if nested {
un.str.WriteString("(")
}
err := un.visit(expr)
if err != nil {
return err
}
if nested {
un.str.WriteString(")")
}
return nil
}
// isLeftRecursive indicates whether the parser resolves the call in a left-recursive manner as
// this can have an effect of how parentheses affect the order of operations in the AST.
func isLeftRecursive(op string) bool {
return op != operators.LogicalAnd && op != operators.LogicalOr
}
// isSamePrecedence indicates whether the precedence of the input operator is the same as the
// precedence of the (possible) operation represented in the input Expr.
//
// If the expr is not a Call, the result is false.
func isSamePrecedence(op string, expr ast.Expr) bool {
if expr.Kind() != ast.CallKind {
return false
}
c := expr.AsCall()
other := c.FunctionName()
return operators.Precedence(op) == operators.Precedence(other)
}
// isLowerPrecedence indicates whether the precedence of the input operator is lower precedence
// than the (possible) operation represented in the input Expr.
//
// If the expr is not a Call, the result is false.
func isLowerPrecedence(op string, expr ast.Expr) bool {
c := expr.AsCall()
other := c.FunctionName()
return operators.Precedence(op) < operators.Precedence(other)
}
// Indicates whether the expr is a complex operator, i.e., a call expression
// with 2 or more arguments.
func isComplexOperator(expr ast.Expr) bool {
if expr.Kind() == ast.CallKind && len(expr.AsCall().Args()) >= 2 {
return true
}
return false
}
// Indicates whether it is a complex operation compared to another.
// expr is *not* considered complex if it is not a call expression or has
// less than two arguments, or if it has a higher precedence than op.
func isComplexOperatorWithRespectTo(op string, expr ast.Expr) bool {
if expr.Kind() != ast.CallKind || len(expr.AsCall().Args()) < 2 {
return false
}
return isLowerPrecedence(op, expr)
}
// Indicate whether this is a binary or ternary operator.
func isBinaryOrTernaryOperator(expr ast.Expr) bool {
if expr.Kind() != ast.CallKind || len(expr.AsCall().Args()) < 2 {
return false
}
_, isBinaryOp := operators.FindReverseBinaryOperator(expr.AsCall().FunctionName())
return isBinaryOp || isSamePrecedence(operators.Conditional, expr)
}
// bytesToOctets converts byte sequences to a string using a three digit octal encoded value
// per byte.
func bytesToOctets(byteVal []byte) string {
var b strings.Builder
for _, c := range byteVal {
fmt.Fprintf(&b, "\\%03o", c)
}
return b.String()
}
// writeOperatorWithWrapping outputs the operator and inserts a newline for operators configured
// in the unparser options.
func (un *unparser) writeOperatorWithWrapping(fun string, unmangled string) bool {
_, wrapOperatorExists := un.options.operatorsToWrapOn[fun]
lineLength := un.str.Len() - un.lastWrappedIndex + len(fun)
if wrapOperatorExists && lineLength >= un.options.wrapOnColumn {
un.lastWrappedIndex = un.str.Len()
// wrapAfterColumnLimit flag dictates whether the newline is placed
// before or after the operator
if un.options.wrapAfterColumnLimit {
// Input: a && b
// Output: a &&\nb
un.str.WriteString(" ")
un.str.WriteString(unmangled)
un.str.WriteString("\n")
} else {
// Input: a && b
// Output: a\n&& b
un.str.WriteString("\n")
un.str.WriteString(unmangled)
un.str.WriteString(" ")
}
return true
}
un.str.WriteString(" ")
un.str.WriteString(unmangled)
un.str.WriteString(" ")
return false
}
// Defined defaults for the unparser options
var (
defaultWrapOnColumn = 80
defaultWrapAfterColumnLimit = true
defaultOperatorsToWrapOn = map[string]bool{
operators.LogicalAnd: true,
operators.LogicalOr: true,
}
)
// UnparserOption is a functional option for configuring the output formatting
// of the Unparse function.
type UnparserOption func(*unparserOption) (*unparserOption, error)
// Internal representation of the UnparserOption type
type unparserOption struct {
wrapOnColumn int
operatorsToWrapOn map[string]bool
wrapAfterColumnLimit bool
}
// WrapOnColumn wraps the output expression when its string length exceeds a specified limit
// for operators set by WrapOnOperators function or by default, "&&" and "||" will be wrapped.
//
// Example usage:
//
// Unparse(expr, sourceInfo, WrapOnColumn(40), WrapOnOperators(Operators.LogicalAnd))
//
// This will insert a newline immediately after the logical AND operator for the below example input:
//
// Input:
// 'my-principal-group' in request.auth.claims && request.auth.claims.iat > now - duration('5m')
//
// Output:
// 'my-principal-group' in request.auth.claims &&
// request.auth.claims.iat > now - duration('5m')
func WrapOnColumn(col int) UnparserOption {
return func(opt *unparserOption) (*unparserOption, error) {
if col < 1 {
return nil, fmt.Errorf("Invalid unparser option. Wrap column value must be greater than or equal to 1. Got %v instead", col)
}
opt.wrapOnColumn = col
return opt, nil
}
}
// WrapOnOperators specifies which operators to perform word wrapping on an output expression when its string length
// exceeds the column limit set by WrapOnColumn function.
//
// Word wrapping is supported on non-unary symbolic operators. Refer to operators.go for the full list
//
// This will replace any previously supplied operators instead of merging them.
func WrapOnOperators(symbols ...string) UnparserOption {
return func(opt *unparserOption) (*unparserOption, error) {
opt.operatorsToWrapOn = make(map[string]bool)
for _, symbol := range symbols {
_, found := operators.FindReverse(symbol)
if !found {
return nil, fmt.Errorf("Invalid unparser option. Unsupported operator: %s", symbol)
}
arity := operators.Arity(symbol)
if arity < 2 {
return nil, fmt.Errorf("Invalid unparser option. Unary operators are unsupported: %s", symbol)
}
opt.operatorsToWrapOn[symbol] = true
}
return opt, nil
}
}
// WrapAfterColumnLimit dictates whether to insert a newline before or after the specified operator
// when word wrapping is performed.
//
// Example usage:
//
// Unparse(expr, sourceInfo, WrapOnColumn(40), WrapOnOperators(Operators.LogicalAnd), WrapAfterColumnLimit(false))
//
// This will insert a newline immediately before the logical AND operator for the below example input, ensuring
// that the length of a line never exceeds the specified column limit:
//
// Input:
// 'my-principal-group' in request.auth.claims && request.auth.claims.iat > now - duration('5m')
//
// Output:
// 'my-principal-group' in request.auth.claims
// && request.auth.claims.iat > now - duration('5m')
func WrapAfterColumnLimit(wrapAfter bool) UnparserOption {
return func(opt *unparserOption) (*unparserOption, error) {
opt.wrapAfterColumnLimit = wrapAfter
return opt, nil
}
}
// Copyright 2018 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package test
import (
"fmt"
"strings"
)
// Compare compares two strings, a for actual, e for expected, and returns true or false. The comparison is done,
// by filtering out whitespace (i.e. space, tabs and newline).
func Compare(a string, e string) bool {
return stripWhitespace(a) == stripWhitespace(e)
}
// DiffMessage creates a diff dump message for test failures.
func DiffMessage(context string, actual interface{}, expected interface{}) string {
return fmt.Sprintf("%s: \ngot %v, \nwanted %v", context, actual, expected)
}
func stripWhitespace(a string) string {
a = strings.Replace(a, " ", "", -1)
a = strings.Replace(a, "\n", "", -1)
a = strings.Replace(a, "\t", "", -1)
return strings.Replace(a, "\r", "", -1)
}
// Copyright 2018 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package test
import (
"github.com/google/cel-go/common/operators"
"google.golang.org/protobuf/proto"
exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1"
structpb "google.golang.org/protobuf/types/known/structpb"
)
// TestExpr packages an Expr with SourceInfo, for testing.
type TestExpr struct {
Expr *exprpb.Expr
SourceInfo *exprpb.SourceInfo
}
// Info returns a copy of the SourceInfo with the given location.
func (t *TestExpr) Info(location string) *exprpb.SourceInfo {
info := proto.Clone(t.SourceInfo).(*exprpb.SourceInfo)
info.Location = location
return info
}
var (
// Empty generates a program with no instructions.
Empty = &TestExpr{
Expr: &exprpb.Expr{},
SourceInfo: &exprpb.SourceInfo{
LineOffsets: []int32{},
Positions: map[int64]int32{}}}
// Exists generates "[1, 1u, 1.0].exists(x, type(x) == uint)".
Exists = &TestExpr{
Expr: ExprComprehension(1,
"x",
ExprList(8,
ExprLiteral(2, int64(0)),
ExprLiteral(3, int64(1)),
ExprLiteral(4, int64(2)),
ExprLiteral(5, int64(3)),
ExprLiteral(6, int64(4)),
ExprLiteral(7, uint64(5))),
"__result__",
ExprLiteral(9, false),
ExprCall(12,
operators.NotStrictlyFalse,
ExprCall(10,
operators.LogicalNot,
ExprIdent(11, "__result__"))),
ExprCall(13,
operators.LogicalOr,
ExprIdent(14, "__result__"),
ExprCall(15,
operators.Equals,
ExprCall(16,
"type",
ExprIdent(17, "x")),
ExprIdent(18, "uint"))),
ExprIdent(19, "__result__")),
SourceInfo: &exprpb.SourceInfo{
LineOffsets: []int32{0},
Positions: map[int64]int32{
0: 12,
1: 0,
2: 1,
3: 4,
4: 8,
5: 0,
6: 18,
7: 18,
8: 18,
9: 18,
10: 18,
11: 20,
12: 20,
13: 28,
14: 28,
15: 28,
16: 28,
17: 28,
18: 28,
19: 28}}}
// ExistsWithInput generates "elems.exists(x, type(x) == uint)".
ExistsWithInput = &TestExpr{
Expr: ExprComprehension(1,
"x",
ExprIdent(2, "elems"),
"__result__",
ExprLiteral(3, false),
ExprCall(4,
operators.LogicalNot,
ExprIdent(5, "__result__")),
ExprCall(6,
operators.Equals,
ExprCall(7,
"type",
ExprIdent(8, "x")),
ExprIdent(9, "uint")),
ExprIdent(10, "__result__")),
SourceInfo: &exprpb.SourceInfo{
LineOffsets: []int32{0},
Positions: map[int64]int32{
0: 12,
1: 0,
2: 1,
3: 4,
4: 8,
5: 0,
6: 18,
7: 18,
8: 18,
9: 18,
10: 18}}}
// DynMap generates a map literal:
// {"hello": "world".size(),
// "dur": duration.Duration{10},
// "ts": timestamp.Timestamp{1000},
// "null": null,
// "bytes": b"bytes-string"}
DynMap = &TestExpr{
Expr: ExprMap(17,
ExprEntry(2,
ExprLiteral(1, "hello"),
ExprMemberCall(3,
"size",
ExprLiteral(4, "world"))),
ExprEntry(12,
ExprLiteral(11, "null"),
ExprLiteral(13, structpb.NullValue_NULL_VALUE)),
ExprEntry(15,
ExprLiteral(14, "bytes"),
ExprLiteral(16, []byte("bytes-string")))),
SourceInfo: &exprpb.SourceInfo{
LineOffsets: []int32{},
Positions: map[int64]int32{}}}
// LogicalAnd generates "a && {c: true}.c".
LogicalAnd = &TestExpr{
ExprCall(2, operators.LogicalAnd,
ExprIdent(1, "a"),
ExprSelect(8,
ExprMap(5,
ExprEntry(4, ExprLiteral(6, "c"), ExprLiteral(7, true))),
"c")),
&exprpb.SourceInfo{
LineOffsets: []int32{},
Positions: map[int64]int32{}}}
// LogicalOr generates "{c: false}.c || a".
LogicalOr = &TestExpr{
ExprCall(2, operators.LogicalOr,
ExprSelect(8,
ExprMap(5,
ExprEntry(4, ExprLiteral(6, "c"), ExprLiteral(7, false))),
"c"),
ExprIdent(1, "a")),
&exprpb.SourceInfo{
LineOffsets: []int32{},
Positions: map[int64]int32{}}}
// LogicalOrEquals generates "a || b == 'b'".
LogicalOrEquals = &TestExpr{
ExprCall(5, operators.LogicalOr,
ExprIdent(1, "a"),
ExprCall(4, operators.Equals,
ExprIdent(2, "b"),
ExprLiteral(3, "b"))),
&exprpb.SourceInfo{
LineOffsets: []int32{},
Positions: map[int64]int32{}}}
// LogicalAndMissingType generates "a && TestProto{c: true}.c" where the
// type 'TestProto' is undefined.
LogicalAndMissingType = &TestExpr{
ExprCall(2, operators.LogicalAnd,
ExprIdent(1, "a"),
ExprSelect(7,
ExprType(5, "TestProto",
ExprField(4, "c", ExprLiteral(6, true))),
"c")),
&exprpb.SourceInfo{
LineOffsets: []int32{},
Positions: map[int64]int32{}}}
// Conditional generates "a ? b < 1.0 : c == ["hello"]".
Conditional = &TestExpr{
Expr: ExprCall(9, operators.Conditional,
ExprIdent(1, "a"),
ExprCall(3,
operators.Less,
ExprIdent(2, "b"),
ExprLiteral(4, 1.0)),
ExprCall(6,
operators.Equals,
ExprIdent(5, "c"),
ExprList(8, ExprLiteral(7, "hello")))),
SourceInfo: &exprpb.SourceInfo{
LineOffsets: []int32{},
Positions: map[int64]int32{}}}
// Select generates "a.b.c".
Select = &TestExpr{
Expr: ExprSelect(3,
ExprSelect(2,
ExprIdent(1, "a"),
"b"),
"c"),
SourceInfo: &exprpb.SourceInfo{
LineOffsets: []int32{},
Positions: map[int64]int32{}}}
// Equality generates "a == 42".
Equality = &TestExpr{
Expr: ExprCall(2,
operators.Equals,
ExprIdent(1, "a"),
ExprLiteral(3, int64(42))),
SourceInfo: &exprpb.SourceInfo{
LineOffsets: []int32{},
Positions: map[int64]int32{}}}
// TypeEquality generates "type(a) == uint".
TypeEquality = &TestExpr{
Expr: ExprCall(4,
operators.Equals,
ExprCall(1, "type",
ExprIdent(2, "a")),
ExprIdent(3, "uint")),
SourceInfo: &exprpb.SourceInfo{
LineOffsets: []int32{},
Positions: map[int64]int32{}}}
)
// ExprIdent creates an ident (variable) Expr.
func ExprIdent(id int64, name string) *exprpb.Expr {
return &exprpb.Expr{Id: id, ExprKind: &exprpb.Expr_IdentExpr{
IdentExpr: &exprpb.Expr_Ident{Name: name}}}
}
// ExprSelect creates a select Expr.
func ExprSelect(id int64, operand *exprpb.Expr, field string) *exprpb.Expr {
return &exprpb.Expr{Id: id,
ExprKind: &exprpb.Expr_SelectExpr{
SelectExpr: &exprpb.Expr_Select{
Operand: operand,
Field: field,
TestOnly: false}}}
}
// ExprLiteral creates a literal (constant) Expr.
func ExprLiteral(id int64, value interface{}) *exprpb.Expr {
var literal *exprpb.Constant
switch value.(type) {
case bool:
literal = &exprpb.Constant{ConstantKind: &exprpb.Constant_BoolValue{
BoolValue: value.(bool)}}
case int64:
literal = &exprpb.Constant{ConstantKind: &exprpb.Constant_Int64Value{
Int64Value: value.(int64)}}
case uint64:
literal = &exprpb.Constant{ConstantKind: &exprpb.Constant_Uint64Value{
Uint64Value: value.(uint64)}}
case float64:
literal = &exprpb.Constant{ConstantKind: &exprpb.Constant_DoubleValue{
DoubleValue: value.(float64)}}
case string:
literal = &exprpb.Constant{ConstantKind: &exprpb.Constant_StringValue{
StringValue: value.(string)}}
case structpb.NullValue:
literal = &exprpb.Constant{ConstantKind: &exprpb.Constant_NullValue{
NullValue: value.(structpb.NullValue)}}
case []byte:
literal = &exprpb.Constant{ConstantKind: &exprpb.Constant_BytesValue{
BytesValue: value.([]byte)}}
default:
panic("literal type not implemented")
}
return &exprpb.Expr{Id: id, ExprKind: &exprpb.Expr_ConstExpr{ConstExpr: literal}}
}
// ExprCall creates a call Expr.
func ExprCall(id int64, function string, args ...*exprpb.Expr) *exprpb.Expr {
return &exprpb.Expr{Id: id,
ExprKind: &exprpb.Expr_CallExpr{
CallExpr: &exprpb.Expr_Call{Target: nil, Function: function, Args: args}}}
}
// ExprMemberCall creates a receiver-style call Expr.
func ExprMemberCall(id int64, function string, target *exprpb.Expr, args ...*exprpb.Expr) *exprpb.Expr {
return &exprpb.Expr{Id: id,
ExprKind: &exprpb.Expr_CallExpr{
CallExpr: &exprpb.Expr_Call{Target: target, Function: function, Args: args}}}
}
// ExprList creates a create list Expr.
func ExprList(id int64, elements ...*exprpb.Expr) *exprpb.Expr {
return &exprpb.Expr{Id: id,
ExprKind: &exprpb.Expr_ListExpr{
ListExpr: &exprpb.Expr_CreateList{Elements: elements}}}
}
// ExprMap creates a create struct Expr for a map.
func ExprMap(id int64, entries ...*exprpb.Expr_CreateStruct_Entry) *exprpb.Expr {
return &exprpb.Expr{Id: id, ExprKind: &exprpb.Expr_StructExpr{
StructExpr: &exprpb.Expr_CreateStruct{Entries: entries}}}
}
// ExprType creates creates a create struct Expr for a message.
func ExprType(id int64, messageName string,
entries ...*exprpb.Expr_CreateStruct_Entry) *exprpb.Expr {
return &exprpb.Expr{Id: id, ExprKind: &exprpb.Expr_StructExpr{
StructExpr: &exprpb.Expr_CreateStruct{
MessageName: messageName, Entries: entries}}}
}
// ExprEntry creates a map entry for a create struct Expr.
func ExprEntry(id int64, key *exprpb.Expr,
value *exprpb.Expr) *exprpb.Expr_CreateStruct_Entry {
return &exprpb.Expr_CreateStruct_Entry{Id: id,
KeyKind: &exprpb.Expr_CreateStruct_Entry_MapKey{MapKey: key},
Value: value}
}
// ExprField creates a field entry for a create struct Expr.
func ExprField(id int64, field string,
value *exprpb.Expr) *exprpb.Expr_CreateStruct_Entry {
return &exprpb.Expr_CreateStruct_Entry{Id: id,
KeyKind: &exprpb.Expr_CreateStruct_Entry_FieldKey{FieldKey: field},
Value: value}
}
// ExprComprehension returns a comprehension Expr.
func ExprComprehension(id int64,
iterVar string, iterRange *exprpb.Expr,
accuVar string, accuInit *exprpb.Expr,
loopCondition *exprpb.Expr, loopStep *exprpb.Expr,
resultExpr *exprpb.Expr) *exprpb.Expr {
return &exprpb.Expr{Id: id,
ExprKind: &exprpb.Expr_ComprehensionExpr{
ComprehensionExpr: &exprpb.Expr_Comprehension{
IterVar: iterVar,
IterRange: iterRange,
AccuVar: accuVar,
AccuInit: accuInit,
LoopCondition: loopCondition,
LoopStep: loopStep,
Result: resultExpr}}}
}
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.28.1
// protoc v3.21.5
// source: test/proto2pb/test_all_types.proto
package proto2pb
import (
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
anypb "google.golang.org/protobuf/types/known/anypb"
durationpb "google.golang.org/protobuf/types/known/durationpb"
structpb "google.golang.org/protobuf/types/known/structpb"
timestamppb "google.golang.org/protobuf/types/known/timestamppb"
wrapperspb "google.golang.org/protobuf/types/known/wrapperspb"
reflect "reflect"
sync "sync"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
type GlobalEnum int32
const (
GlobalEnum_GOO GlobalEnum = 0
GlobalEnum_GAR GlobalEnum = 1
GlobalEnum_GAZ GlobalEnum = 2
)
// Enum value maps for GlobalEnum.
var (
GlobalEnum_name = map[int32]string{
0: "GOO",
1: "GAR",
2: "GAZ",
}
GlobalEnum_value = map[string]int32{
"GOO": 0,
"GAR": 1,
"GAZ": 2,
}
)
func (x GlobalEnum) Enum() *GlobalEnum {
p := new(GlobalEnum)
*p = x
return p
}
func (x GlobalEnum) String() string {
return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
}
func (GlobalEnum) Descriptor() protoreflect.EnumDescriptor {
return file_test_proto2pb_test_all_types_proto_enumTypes[0].Descriptor()
}
func (GlobalEnum) Type() protoreflect.EnumType {
return &file_test_proto2pb_test_all_types_proto_enumTypes[0]
}
func (x GlobalEnum) Number() protoreflect.EnumNumber {
return protoreflect.EnumNumber(x)
}
// Deprecated: Do not use.
func (x *GlobalEnum) UnmarshalJSON(b []byte) error {
num, err := protoimpl.X.UnmarshalJSONEnum(x.Descriptor(), b)
if err != nil {
return err
}
*x = GlobalEnum(num)
return nil
}
// Deprecated: Use GlobalEnum.Descriptor instead.
func (GlobalEnum) EnumDescriptor() ([]byte, []int) {
return file_test_proto2pb_test_all_types_proto_rawDescGZIP(), []int{0}
}
type TestAllTypes_NestedEnum int32
const (
TestAllTypes_FOO TestAllTypes_NestedEnum = 0
TestAllTypes_BAR TestAllTypes_NestedEnum = 1
TestAllTypes_BAZ TestAllTypes_NestedEnum = 2
)
// Enum value maps for TestAllTypes_NestedEnum.
var (
TestAllTypes_NestedEnum_name = map[int32]string{
0: "FOO",
1: "BAR",
2: "BAZ",
}
TestAllTypes_NestedEnum_value = map[string]int32{
"FOO": 0,
"BAR": 1,
"BAZ": 2,
}
)
func (x TestAllTypes_NestedEnum) Enum() *TestAllTypes_NestedEnum {
p := new(TestAllTypes_NestedEnum)
*p = x
return p
}
func (x TestAllTypes_NestedEnum) String() string {
return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
}
func (TestAllTypes_NestedEnum) Descriptor() protoreflect.EnumDescriptor {
return file_test_proto2pb_test_all_types_proto_enumTypes[1].Descriptor()
}
func (TestAllTypes_NestedEnum) Type() protoreflect.EnumType {
return &file_test_proto2pb_test_all_types_proto_enumTypes[1]
}
func (x TestAllTypes_NestedEnum) Number() protoreflect.EnumNumber {
return protoreflect.EnumNumber(x)
}
// Deprecated: Do not use.
func (x *TestAllTypes_NestedEnum) UnmarshalJSON(b []byte) error {
num, err := protoimpl.X.UnmarshalJSONEnum(x.Descriptor(), b)
if err != nil {
return err
}
*x = TestAllTypes_NestedEnum(num)
return nil
}
// Deprecated: Use TestAllTypes_NestedEnum.Descriptor instead.
func (TestAllTypes_NestedEnum) EnumDescriptor() ([]byte, []int) {
return file_test_proto2pb_test_all_types_proto_rawDescGZIP(), []int{0, 0}
}
type TestAllTypes struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
SingleInt32 *int32 `protobuf:"varint,1,opt,name=single_int32,json=singleInt32,def=-32" json:"single_int32,omitempty"`
SingleInt64 *int64 `protobuf:"varint,2,opt,name=single_int64,json=singleInt64,def=-64" json:"single_int64,omitempty"`
SingleUint32 *uint32 `protobuf:"varint,3,opt,name=single_uint32,json=singleUint32,def=32" json:"single_uint32,omitempty"`
SingleUint64 *uint64 `protobuf:"varint,4,opt,name=single_uint64,json=singleUint64,def=64" json:"single_uint64,omitempty"`
SingleSint32 *int32 `protobuf:"zigzag32,5,opt,name=single_sint32,json=singleSint32" json:"single_sint32,omitempty"`
SingleSint64 *int64 `protobuf:"zigzag64,6,opt,name=single_sint64,json=singleSint64" json:"single_sint64,omitempty"`
SingleFixed32 *uint32 `protobuf:"fixed32,7,opt,name=single_fixed32,json=singleFixed32" json:"single_fixed32,omitempty"`
SingleFixed64 *uint64 `protobuf:"fixed64,8,opt,name=single_fixed64,json=singleFixed64" json:"single_fixed64,omitempty"`
SingleSfixed32 *int32 `protobuf:"fixed32,9,opt,name=single_sfixed32,json=singleSfixed32" json:"single_sfixed32,omitempty"`
SingleSfixed64 *int64 `protobuf:"fixed64,10,opt,name=single_sfixed64,json=singleSfixed64" json:"single_sfixed64,omitempty"`
SingleFloat *float32 `protobuf:"fixed32,11,opt,name=single_float,json=singleFloat,def=3" json:"single_float,omitempty"`
SingleDouble *float64 `protobuf:"fixed64,12,opt,name=single_double,json=singleDouble,def=6.4" json:"single_double,omitempty"`
SingleBool *bool `protobuf:"varint,13,opt,name=single_bool,json=singleBool,def=1" json:"single_bool,omitempty"`
SingleString *string `protobuf:"bytes,14,opt,name=single_string,json=singleString,def=empty" json:"single_string,omitempty"`
SingleBytes []byte `protobuf:"bytes,15,opt,name=single_bytes,json=singleBytes,def=none" json:"single_bytes,omitempty"`
StandaloneEnum *TestAllTypes_NestedEnum `protobuf:"varint,22,opt,name=standalone_enum,json=standaloneEnum,enum=google.expr.proto2.test.TestAllTypes_NestedEnum" json:"standalone_enum,omitempty"`
Nestedgroup *TestAllTypes_NestedGroup `protobuf:"group,23,opt,name=NestedGroup,json=nestedgroup" json:"nestedgroup,omitempty"`
SingleAny *anypb.Any `protobuf:"bytes,100,opt,name=single_any,json=singleAny" json:"single_any,omitempty"`
SingleDuration *durationpb.Duration `protobuf:"bytes,101,opt,name=single_duration,json=singleDuration" json:"single_duration,omitempty"`
SingleTimestamp *timestamppb.Timestamp `protobuf:"bytes,102,opt,name=single_timestamp,json=singleTimestamp" json:"single_timestamp,omitempty"`
SingleStruct *structpb.Struct `protobuf:"bytes,103,opt,name=single_struct,json=singleStruct" json:"single_struct,omitempty"`
SingleValue *structpb.Value `protobuf:"bytes,104,opt,name=single_value,json=singleValue" json:"single_value,omitempty"`
SingleInt64Wrapper *wrapperspb.Int64Value `protobuf:"bytes,105,opt,name=single_int64_wrapper,json=singleInt64Wrapper" json:"single_int64_wrapper,omitempty"`
SingleInt32Wrapper *wrapperspb.Int32Value `protobuf:"bytes,106,opt,name=single_int32_wrapper,json=singleInt32Wrapper" json:"single_int32_wrapper,omitempty"`
SingleDoubleWrapper *wrapperspb.DoubleValue `protobuf:"bytes,107,opt,name=single_double_wrapper,json=singleDoubleWrapper" json:"single_double_wrapper,omitempty"`
SingleFloatWrapper *wrapperspb.FloatValue `protobuf:"bytes,108,opt,name=single_float_wrapper,json=singleFloatWrapper" json:"single_float_wrapper,omitempty"`
SingleUint64Wrapper *wrapperspb.UInt64Value `protobuf:"bytes,109,opt,name=single_uint64_wrapper,json=singleUint64Wrapper" json:"single_uint64_wrapper,omitempty"`
SingleUint32Wrapper *wrapperspb.UInt32Value `protobuf:"bytes,110,opt,name=single_uint32_wrapper,json=singleUint32Wrapper" json:"single_uint32_wrapper,omitempty"`
SingleStringWrapper *wrapperspb.StringValue `protobuf:"bytes,111,opt,name=single_string_wrapper,json=singleStringWrapper" json:"single_string_wrapper,omitempty"`
SingleBoolWrapper *wrapperspb.BoolValue `protobuf:"bytes,112,opt,name=single_bool_wrapper,json=singleBoolWrapper" json:"single_bool_wrapper,omitempty"`
SingleBytesWrapper *wrapperspb.BytesValue `protobuf:"bytes,113,opt,name=single_bytes_wrapper,json=singleBytesWrapper" json:"single_bytes_wrapper,omitempty"`
// Types that are assignable to NestedType:
//
// *TestAllTypes_SingleNestedMessage
// *TestAllTypes_SingleNestedEnum
NestedType isTestAllTypes_NestedType `protobuf_oneof:"nested_type"`
RepeatedInt32 []int32 `protobuf:"varint,31,rep,name=repeated_int32,json=repeatedInt32" json:"repeated_int32,omitempty"`
RepeatedInt64 []int64 `protobuf:"varint,32,rep,name=repeated_int64,json=repeatedInt64" json:"repeated_int64,omitempty"`
RepeatedUint32 []uint32 `protobuf:"varint,33,rep,name=repeated_uint32,json=repeatedUint32" json:"repeated_uint32,omitempty"`
RepeatedUint64 []uint64 `protobuf:"varint,34,rep,name=repeated_uint64,json=repeatedUint64" json:"repeated_uint64,omitempty"`
RepeatedSint32 []int32 `protobuf:"zigzag32,35,rep,name=repeated_sint32,json=repeatedSint32" json:"repeated_sint32,omitempty"`
RepeatedSint64 []int64 `protobuf:"zigzag64,36,rep,name=repeated_sint64,json=repeatedSint64" json:"repeated_sint64,omitempty"`
RepeatedFixed32 []uint32 `protobuf:"fixed32,37,rep,name=repeated_fixed32,json=repeatedFixed32" json:"repeated_fixed32,omitempty"`
RepeatedFixed64 []uint64 `protobuf:"fixed64,38,rep,name=repeated_fixed64,json=repeatedFixed64" json:"repeated_fixed64,omitempty"`
RepeatedSfixed32 []int32 `protobuf:"fixed32,39,rep,name=repeated_sfixed32,json=repeatedSfixed32" json:"repeated_sfixed32,omitempty"`
RepeatedSfixed64 []int64 `protobuf:"fixed64,40,rep,name=repeated_sfixed64,json=repeatedSfixed64" json:"repeated_sfixed64,omitempty"`
RepeatedFloat []float32 `protobuf:"fixed32,41,rep,name=repeated_float,json=repeatedFloat" json:"repeated_float,omitempty"`
RepeatedDouble []float64 `protobuf:"fixed64,42,rep,name=repeated_double,json=repeatedDouble" json:"repeated_double,omitempty"`
RepeatedBool []bool `protobuf:"varint,43,rep,name=repeated_bool,json=repeatedBool" json:"repeated_bool,omitempty"`
RepeatedString []string `protobuf:"bytes,44,rep,name=repeated_string,json=repeatedString" json:"repeated_string,omitempty"`
RepeatedBytes [][]byte `protobuf:"bytes,45,rep,name=repeated_bytes,json=repeatedBytes" json:"repeated_bytes,omitempty"`
RepeatedNestedMessage []*TestAllTypes_NestedMessage `protobuf:"bytes,48,rep,name=repeated_nested_message,json=repeatedNestedMessage" json:"repeated_nested_message,omitempty"`
RepeatedNestedEnum []TestAllTypes_NestedEnum `protobuf:"varint,51,rep,name=repeated_nested_enum,json=repeatedNestedEnum,enum=google.expr.proto2.test.TestAllTypes_NestedEnum" json:"repeated_nested_enum,omitempty"`
RepeatedStringPiece []string `protobuf:"bytes,54,rep,name=repeated_string_piece,json=repeatedStringPiece" json:"repeated_string_piece,omitempty"`
RepeatedCord []string `protobuf:"bytes,55,rep,name=repeated_cord,json=repeatedCord" json:"repeated_cord,omitempty"`
RepeatedLazyMessage []*TestAllTypes_NestedMessage `protobuf:"bytes,57,rep,name=repeated_lazy_message,json=repeatedLazyMessage" json:"repeated_lazy_message,omitempty"`
MapStringString map[string]string `protobuf:"bytes,58,rep,name=map_string_string,json=mapStringString" json:"map_string_string,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"`
MapInt64NestedType map[int64]*NestedTestAllTypes `protobuf:"bytes,59,rep,name=map_int64_nested_type,json=mapInt64NestedType" json:"map_int64_nested_type,omitempty" protobuf_key:"varint,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"`
}
// Default values for TestAllTypes fields.
const (
Default_TestAllTypes_SingleInt32 = int32(-32)
Default_TestAllTypes_SingleInt64 = int64(-64)
Default_TestAllTypes_SingleUint32 = uint32(32)
Default_TestAllTypes_SingleUint64 = uint64(64)
Default_TestAllTypes_SingleFloat = float32(3)
Default_TestAllTypes_SingleDouble = float64(6.4)
Default_TestAllTypes_SingleBool = bool(true)
Default_TestAllTypes_SingleString = string("empty")
Default_TestAllTypes_SingleNestedEnum = TestAllTypes_BAR
)
// Default values for TestAllTypes fields.
var (
Default_TestAllTypes_SingleBytes = []byte("none")
)
func (x *TestAllTypes) Reset() {
*x = TestAllTypes{}
if protoimpl.UnsafeEnabled {
mi := &file_test_proto2pb_test_all_types_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *TestAllTypes) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*TestAllTypes) ProtoMessage() {}
func (x *TestAllTypes) ProtoReflect() protoreflect.Message {
mi := &file_test_proto2pb_test_all_types_proto_msgTypes[0]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use TestAllTypes.ProtoReflect.Descriptor instead.
func (*TestAllTypes) Descriptor() ([]byte, []int) {
return file_test_proto2pb_test_all_types_proto_rawDescGZIP(), []int{0}
}
func (x *TestAllTypes) GetSingleInt32() int32 {
if x != nil && x.SingleInt32 != nil {
return *x.SingleInt32
}
return Default_TestAllTypes_SingleInt32
}
func (x *TestAllTypes) GetSingleInt64() int64 {
if x != nil && x.SingleInt64 != nil {
return *x.SingleInt64
}
return Default_TestAllTypes_SingleInt64
}
func (x *TestAllTypes) GetSingleUint32() uint32 {
if x != nil && x.SingleUint32 != nil {
return *x.SingleUint32
}
return Default_TestAllTypes_SingleUint32
}
func (x *TestAllTypes) GetSingleUint64() uint64 {
if x != nil && x.SingleUint64 != nil {
return *x.SingleUint64
}
return Default_TestAllTypes_SingleUint64
}
func (x *TestAllTypes) GetSingleSint32() int32 {
if x != nil && x.SingleSint32 != nil {
return *x.SingleSint32
}
return 0
}
func (x *TestAllTypes) GetSingleSint64() int64 {
if x != nil && x.SingleSint64 != nil {
return *x.SingleSint64
}
return 0
}
func (x *TestAllTypes) GetSingleFixed32() uint32 {
if x != nil && x.SingleFixed32 != nil {
return *x.SingleFixed32
}
return 0
}
func (x *TestAllTypes) GetSingleFixed64() uint64 {
if x != nil && x.SingleFixed64 != nil {
return *x.SingleFixed64
}
return 0
}
func (x *TestAllTypes) GetSingleSfixed32() int32 {
if x != nil && x.SingleSfixed32 != nil {
return *x.SingleSfixed32
}
return 0
}
func (x *TestAllTypes) GetSingleSfixed64() int64 {
if x != nil && x.SingleSfixed64 != nil {
return *x.SingleSfixed64
}
return 0
}
func (x *TestAllTypes) GetSingleFloat() float32 {
if x != nil && x.SingleFloat != nil {
return *x.SingleFloat
}
return Default_TestAllTypes_SingleFloat
}
func (x *TestAllTypes) GetSingleDouble() float64 {
if x != nil && x.SingleDouble != nil {
return *x.SingleDouble
}
return Default_TestAllTypes_SingleDouble
}
func (x *TestAllTypes) GetSingleBool() bool {
if x != nil && x.SingleBool != nil {
return *x.SingleBool
}
return Default_TestAllTypes_SingleBool
}
func (x *TestAllTypes) GetSingleString() string {
if x != nil && x.SingleString != nil {
return *x.SingleString
}
return Default_TestAllTypes_SingleString
}
func (x *TestAllTypes) GetSingleBytes() []byte {
if x != nil && x.SingleBytes != nil {
return x.SingleBytes
}
return append([]byte(nil), Default_TestAllTypes_SingleBytes...)
}
func (x *TestAllTypes) GetStandaloneEnum() TestAllTypes_NestedEnum {
if x != nil && x.StandaloneEnum != nil {
return *x.StandaloneEnum
}
return TestAllTypes_FOO
}
func (x *TestAllTypes) GetNestedgroup() *TestAllTypes_NestedGroup {
if x != nil {
return x.Nestedgroup
}
return nil
}
func (x *TestAllTypes) GetSingleAny() *anypb.Any {
if x != nil {
return x.SingleAny
}
return nil
}
func (x *TestAllTypes) GetSingleDuration() *durationpb.Duration {
if x != nil {
return x.SingleDuration
}
return nil
}
func (x *TestAllTypes) GetSingleTimestamp() *timestamppb.Timestamp {
if x != nil {
return x.SingleTimestamp
}
return nil
}
func (x *TestAllTypes) GetSingleStruct() *structpb.Struct {
if x != nil {
return x.SingleStruct
}
return nil
}
func (x *TestAllTypes) GetSingleValue() *structpb.Value {
if x != nil {
return x.SingleValue
}
return nil
}
func (x *TestAllTypes) GetSingleInt64Wrapper() *wrapperspb.Int64Value {
if x != nil {
return x.SingleInt64Wrapper
}
return nil
}
func (x *TestAllTypes) GetSingleInt32Wrapper() *wrapperspb.Int32Value {
if x != nil {
return x.SingleInt32Wrapper
}
return nil
}
func (x *TestAllTypes) GetSingleDoubleWrapper() *wrapperspb.DoubleValue {
if x != nil {
return x.SingleDoubleWrapper
}
return nil
}
func (x *TestAllTypes) GetSingleFloatWrapper() *wrapperspb.FloatValue {
if x != nil {
return x.SingleFloatWrapper
}
return nil
}
func (x *TestAllTypes) GetSingleUint64Wrapper() *wrapperspb.UInt64Value {
if x != nil {
return x.SingleUint64Wrapper
}
return nil
}
func (x *TestAllTypes) GetSingleUint32Wrapper() *wrapperspb.UInt32Value {
if x != nil {
return x.SingleUint32Wrapper
}
return nil
}
func (x *TestAllTypes) GetSingleStringWrapper() *wrapperspb.StringValue {
if x != nil {
return x.SingleStringWrapper
}
return nil
}
func (x *TestAllTypes) GetSingleBoolWrapper() *wrapperspb.BoolValue {
if x != nil {
return x.SingleBoolWrapper
}
return nil
}
func (x *TestAllTypes) GetSingleBytesWrapper() *wrapperspb.BytesValue {
if x != nil {
return x.SingleBytesWrapper
}
return nil
}
func (m *TestAllTypes) GetNestedType() isTestAllTypes_NestedType {
if m != nil {
return m.NestedType
}
return nil
}
func (x *TestAllTypes) GetSingleNestedMessage() *TestAllTypes_NestedMessage {
if x, ok := x.GetNestedType().(*TestAllTypes_SingleNestedMessage); ok {
return x.SingleNestedMessage
}
return nil
}
func (x *TestAllTypes) GetSingleNestedEnum() TestAllTypes_NestedEnum {
if x, ok := x.GetNestedType().(*TestAllTypes_SingleNestedEnum); ok {
return x.SingleNestedEnum
}
return Default_TestAllTypes_SingleNestedEnum
}
func (x *TestAllTypes) GetRepeatedInt32() []int32 {
if x != nil {
return x.RepeatedInt32
}
return nil
}
func (x *TestAllTypes) GetRepeatedInt64() []int64 {
if x != nil {
return x.RepeatedInt64
}
return nil
}
func (x *TestAllTypes) GetRepeatedUint32() []uint32 {
if x != nil {
return x.RepeatedUint32
}
return nil
}
func (x *TestAllTypes) GetRepeatedUint64() []uint64 {
if x != nil {
return x.RepeatedUint64
}
return nil
}
func (x *TestAllTypes) GetRepeatedSint32() []int32 {
if x != nil {
return x.RepeatedSint32
}
return nil
}
func (x *TestAllTypes) GetRepeatedSint64() []int64 {
if x != nil {
return x.RepeatedSint64
}
return nil
}
func (x *TestAllTypes) GetRepeatedFixed32() []uint32 {
if x != nil {
return x.RepeatedFixed32
}
return nil
}
func (x *TestAllTypes) GetRepeatedFixed64() []uint64 {
if x != nil {
return x.RepeatedFixed64
}
return nil
}
func (x *TestAllTypes) GetRepeatedSfixed32() []int32 {
if x != nil {
return x.RepeatedSfixed32
}
return nil
}
func (x *TestAllTypes) GetRepeatedSfixed64() []int64 {
if x != nil {
return x.RepeatedSfixed64
}
return nil
}
func (x *TestAllTypes) GetRepeatedFloat() []float32 {
if x != nil {
return x.RepeatedFloat
}
return nil
}
func (x *TestAllTypes) GetRepeatedDouble() []float64 {
if x != nil {
return x.RepeatedDouble
}
return nil
}
func (x *TestAllTypes) GetRepeatedBool() []bool {
if x != nil {
return x.RepeatedBool
}
return nil
}
func (x *TestAllTypes) GetRepeatedString() []string {
if x != nil {
return x.RepeatedString
}
return nil
}
func (x *TestAllTypes) GetRepeatedBytes() [][]byte {
if x != nil {
return x.RepeatedBytes
}
return nil
}
func (x *TestAllTypes) GetRepeatedNestedMessage() []*TestAllTypes_NestedMessage {
if x != nil {
return x.RepeatedNestedMessage
}
return nil
}
func (x *TestAllTypes) GetRepeatedNestedEnum() []TestAllTypes_NestedEnum {
if x != nil {
return x.RepeatedNestedEnum
}
return nil
}
func (x *TestAllTypes) GetRepeatedStringPiece() []string {
if x != nil {
return x.RepeatedStringPiece
}
return nil
}
func (x *TestAllTypes) GetRepeatedCord() []string {
if x != nil {
return x.RepeatedCord
}
return nil
}
func (x *TestAllTypes) GetRepeatedLazyMessage() []*TestAllTypes_NestedMessage {
if x != nil {
return x.RepeatedLazyMessage
}
return nil
}
func (x *TestAllTypes) GetMapStringString() map[string]string {
if x != nil {
return x.MapStringString
}
return nil
}
func (x *TestAllTypes) GetMapInt64NestedType() map[int64]*NestedTestAllTypes {
if x != nil {
return x.MapInt64NestedType
}
return nil
}
type isTestAllTypes_NestedType interface {
isTestAllTypes_NestedType()
}
type TestAllTypes_SingleNestedMessage struct {
SingleNestedMessage *TestAllTypes_NestedMessage `protobuf:"bytes,18,opt,name=single_nested_message,json=singleNestedMessage,oneof"`
}
type TestAllTypes_SingleNestedEnum struct {
SingleNestedEnum TestAllTypes_NestedEnum `protobuf:"varint,21,opt,name=single_nested_enum,json=singleNestedEnum,enum=google.expr.proto2.test.TestAllTypes_NestedEnum,oneof,def=1"`
}
func (*TestAllTypes_SingleNestedMessage) isTestAllTypes_NestedType() {}
func (*TestAllTypes_SingleNestedEnum) isTestAllTypes_NestedType() {}
type NestedTestAllTypes struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Child *NestedTestAllTypes `protobuf:"bytes,1,opt,name=child" json:"child,omitempty"`
Payload *TestAllTypes `protobuf:"bytes,2,opt,name=payload" json:"payload,omitempty"`
}
func (x *NestedTestAllTypes) Reset() {
*x = NestedTestAllTypes{}
if protoimpl.UnsafeEnabled {
mi := &file_test_proto2pb_test_all_types_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *NestedTestAllTypes) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*NestedTestAllTypes) ProtoMessage() {}
func (x *NestedTestAllTypes) ProtoReflect() protoreflect.Message {
mi := &file_test_proto2pb_test_all_types_proto_msgTypes[1]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use NestedTestAllTypes.ProtoReflect.Descriptor instead.
func (*NestedTestAllTypes) Descriptor() ([]byte, []int) {
return file_test_proto2pb_test_all_types_proto_rawDescGZIP(), []int{1}
}
func (x *NestedTestAllTypes) GetChild() *NestedTestAllTypes {
if x != nil {
return x.Child
}
return nil
}
func (x *NestedTestAllTypes) GetPayload() *TestAllTypes {
if x != nil {
return x.Payload
}
return nil
}
type ExampleType struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
extensionFields protoimpl.ExtensionFields
Name *string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"`
In *int64 `protobuf:"varint,2,opt,name=in" json:"in,omitempty"`
}
func (x *ExampleType) Reset() {
*x = ExampleType{}
if protoimpl.UnsafeEnabled {
mi := &file_test_proto2pb_test_all_types_proto_msgTypes[2]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *ExampleType) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*ExampleType) ProtoMessage() {}
func (x *ExampleType) ProtoReflect() protoreflect.Message {
mi := &file_test_proto2pb_test_all_types_proto_msgTypes[2]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use ExampleType.ProtoReflect.Descriptor instead.
func (*ExampleType) Descriptor() ([]byte, []int) {
return file_test_proto2pb_test_all_types_proto_rawDescGZIP(), []int{2}
}
func (x *ExampleType) GetName() string {
if x != nil && x.Name != nil {
return *x.Name
}
return ""
}
func (x *ExampleType) GetIn() int64 {
if x != nil && x.In != nil {
return *x.In
}
return 0
}
type ExtendedExampleType struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
}
func (x *ExtendedExampleType) Reset() {
*x = ExtendedExampleType{}
if protoimpl.UnsafeEnabled {
mi := &file_test_proto2pb_test_all_types_proto_msgTypes[3]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *ExtendedExampleType) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*ExtendedExampleType) ProtoMessage() {}
func (x *ExtendedExampleType) ProtoReflect() protoreflect.Message {
mi := &file_test_proto2pb_test_all_types_proto_msgTypes[3]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use ExtendedExampleType.ProtoReflect.Descriptor instead.
func (*ExtendedExampleType) Descriptor() ([]byte, []int) {
return file_test_proto2pb_test_all_types_proto_rawDescGZIP(), []int{3}
}
type TestAllTypes_NestedMessage struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Bb *int32 `protobuf:"varint,1,opt,name=bb" json:"bb,omitempty"`
}
func (x *TestAllTypes_NestedMessage) Reset() {
*x = TestAllTypes_NestedMessage{}
if protoimpl.UnsafeEnabled {
mi := &file_test_proto2pb_test_all_types_proto_msgTypes[4]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *TestAllTypes_NestedMessage) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*TestAllTypes_NestedMessage) ProtoMessage() {}
func (x *TestAllTypes_NestedMessage) ProtoReflect() protoreflect.Message {
mi := &file_test_proto2pb_test_all_types_proto_msgTypes[4]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use TestAllTypes_NestedMessage.ProtoReflect.Descriptor instead.
func (*TestAllTypes_NestedMessage) Descriptor() ([]byte, []int) {
return file_test_proto2pb_test_all_types_proto_rawDescGZIP(), []int{0, 0}
}
func (x *TestAllTypes_NestedMessage) GetBb() int32 {
if x != nil && x.Bb != nil {
return *x.Bb
}
return 0
}
type TestAllTypes_NestedGroup struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
NestedId *int32 `protobuf:"varint,24,opt,name=nested_id,json=nestedId" json:"nested_id,omitempty"`
NestedName *string `protobuf:"bytes,25,opt,name=nested_name,json=nestedName" json:"nested_name,omitempty"`
}
func (x *TestAllTypes_NestedGroup) Reset() {
*x = TestAllTypes_NestedGroup{}
if protoimpl.UnsafeEnabled {
mi := &file_test_proto2pb_test_all_types_proto_msgTypes[5]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *TestAllTypes_NestedGroup) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*TestAllTypes_NestedGroup) ProtoMessage() {}
func (x *TestAllTypes_NestedGroup) ProtoReflect() protoreflect.Message {
mi := &file_test_proto2pb_test_all_types_proto_msgTypes[5]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use TestAllTypes_NestedGroup.ProtoReflect.Descriptor instead.
func (*TestAllTypes_NestedGroup) Descriptor() ([]byte, []int) {
return file_test_proto2pb_test_all_types_proto_rawDescGZIP(), []int{0, 1}
}
func (x *TestAllTypes_NestedGroup) GetNestedId() int32 {
if x != nil && x.NestedId != nil {
return *x.NestedId
}
return 0
}
func (x *TestAllTypes_NestedGroup) GetNestedName() string {
if x != nil && x.NestedName != nil {
return *x.NestedName
}
return ""
}
var file_test_proto2pb_test_all_types_proto_extTypes = []protoimpl.ExtensionInfo{
{
ExtendedType: (*ExampleType)(nil),
ExtensionType: ([]string)(nil),
Field: 103,
Name: "google.expr.proto2.test.ExtendedExampleType.extended_examples",
Tag: "bytes,103,rep,name=extended_examples",
Filename: "test/proto2pb/test_all_types.proto",
},
{
ExtendedType: (*ExampleType)(nil),
ExtensionType: (*GlobalEnum)(nil),
Field: 104,
Name: "google.expr.proto2.test.ExtendedExampleType.enum_ext",
Tag: "varint,104,opt,name=enum_ext,enum=google.expr.proto2.test.GlobalEnum",
Filename: "test/proto2pb/test_all_types.proto",
},
}
// Extension fields to ExampleType.
var (
// repeated string extended_examples = 103;
E_ExtendedExampleType_ExtendedExamples = &file_test_proto2pb_test_all_types_proto_extTypes[0]
// optional google.expr.proto2.test.GlobalEnum enum_ext = 104;
E_ExtendedExampleType_EnumExt = &file_test_proto2pb_test_all_types_proto_extTypes[1]
)
var File_test_proto2pb_test_all_types_proto protoreflect.FileDescriptor
var file_test_proto2pb_test_all_types_proto_rawDesc = []byte{
0x0a, 0x22, 0x74, 0x65, 0x73, 0x74, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x32, 0x70, 0x62, 0x2f,
0x74, 0x65, 0x73, 0x74, 0x5f, 0x61, 0x6c, 0x6c, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x70,
0x72, 0x6f, 0x74, 0x6f, 0x12, 0x17, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x65, 0x78, 0x70,
0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x32, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x67,
0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x61,
0x6e, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65,
0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69,
0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1c, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65,
0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74,
0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70,
0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d,
0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f,
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x72,
0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xab, 0x1c, 0x0a, 0x0c, 0x54, 0x65, 0x73, 0x74,
0x41, 0x6c, 0x6c, 0x54, 0x79, 0x70, 0x65, 0x73, 0x12, 0x26, 0x0a, 0x0c, 0x73, 0x69, 0x6e, 0x67,
0x6c, 0x65, 0x5f, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x3a, 0x03,
0x2d, 0x33, 0x32, 0x52, 0x0b, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x49, 0x6e, 0x74, 0x33, 0x32,
0x12, 0x26, 0x0a, 0x0c, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x5f, 0x69, 0x6e, 0x74, 0x36, 0x34,
0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x3a, 0x03, 0x2d, 0x36, 0x34, 0x52, 0x0b, 0x73, 0x69, 0x6e,
0x67, 0x6c, 0x65, 0x49, 0x6e, 0x74, 0x36, 0x34, 0x12, 0x27, 0x0a, 0x0d, 0x73, 0x69, 0x6e, 0x67,
0x6c, 0x65, 0x5f, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x3a,
0x02, 0x33, 0x32, 0x52, 0x0c, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x55, 0x69, 0x6e, 0x74, 0x33,
0x32, 0x12, 0x27, 0x0a, 0x0d, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x5f, 0x75, 0x69, 0x6e, 0x74,
0x36, 0x34, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x3a, 0x02, 0x36, 0x34, 0x52, 0x0c, 0x73, 0x69,
0x6e, 0x67, 0x6c, 0x65, 0x55, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x12, 0x23, 0x0a, 0x0d, 0x73, 0x69,
0x6e, 0x67, 0x6c, 0x65, 0x5f, 0x73, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x18, 0x05, 0x20, 0x01, 0x28,
0x11, 0x52, 0x0c, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x53, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x12,
0x23, 0x0a, 0x0d, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x5f, 0x73, 0x69, 0x6e, 0x74, 0x36, 0x34,
0x18, 0x06, 0x20, 0x01, 0x28, 0x12, 0x52, 0x0c, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x53, 0x69,
0x6e, 0x74, 0x36, 0x34, 0x12, 0x25, 0x0a, 0x0e, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x5f, 0x66,
0x69, 0x78, 0x65, 0x64, 0x33, 0x32, 0x18, 0x07, 0x20, 0x01, 0x28, 0x07, 0x52, 0x0d, 0x73, 0x69,
0x6e, 0x67, 0x6c, 0x65, 0x46, 0x69, 0x78, 0x65, 0x64, 0x33, 0x32, 0x12, 0x25, 0x0a, 0x0e, 0x73,
0x69, 0x6e, 0x67, 0x6c, 0x65, 0x5f, 0x66, 0x69, 0x78, 0x65, 0x64, 0x36, 0x34, 0x18, 0x08, 0x20,
0x01, 0x28, 0x06, 0x52, 0x0d, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x46, 0x69, 0x78, 0x65, 0x64,
0x36, 0x34, 0x12, 0x27, 0x0a, 0x0f, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x5f, 0x73, 0x66, 0x69,
0x78, 0x65, 0x64, 0x33, 0x32, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0f, 0x52, 0x0e, 0x73, 0x69, 0x6e,
0x67, 0x6c, 0x65, 0x53, 0x66, 0x69, 0x78, 0x65, 0x64, 0x33, 0x32, 0x12, 0x27, 0x0a, 0x0f, 0x73,
0x69, 0x6e, 0x67, 0x6c, 0x65, 0x5f, 0x73, 0x66, 0x69, 0x78, 0x65, 0x64, 0x36, 0x34, 0x18, 0x0a,
0x20, 0x01, 0x28, 0x10, 0x52, 0x0e, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x53, 0x66, 0x69, 0x78,
0x65, 0x64, 0x36, 0x34, 0x12, 0x24, 0x0a, 0x0c, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x5f, 0x66,
0x6c, 0x6f, 0x61, 0x74, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x02, 0x3a, 0x01, 0x33, 0x52, 0x0b, 0x73,
0x69, 0x6e, 0x67, 0x6c, 0x65, 0x46, 0x6c, 0x6f, 0x61, 0x74, 0x12, 0x28, 0x0a, 0x0d, 0x73, 0x69,
0x6e, 0x67, 0x6c, 0x65, 0x5f, 0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x18, 0x0c, 0x20, 0x01, 0x28,
0x01, 0x3a, 0x03, 0x36, 0x2e, 0x34, 0x52, 0x0c, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x44, 0x6f,
0x75, 0x62, 0x6c, 0x65, 0x12, 0x25, 0x0a, 0x0b, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x5f, 0x62,
0x6f, 0x6f, 0x6c, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x08, 0x3a, 0x04, 0x74, 0x72, 0x75, 0x65, 0x52,
0x0a, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x42, 0x6f, 0x6f, 0x6c, 0x12, 0x2a, 0x0a, 0x0d, 0x73,
0x69, 0x6e, 0x67, 0x6c, 0x65, 0x5f, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x18, 0x0e, 0x20, 0x01,
0x28, 0x09, 0x3a, 0x05, 0x65, 0x6d, 0x70, 0x74, 0x79, 0x52, 0x0c, 0x73, 0x69, 0x6e, 0x67, 0x6c,
0x65, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x12, 0x27, 0x0a, 0x0c, 0x73, 0x69, 0x6e, 0x67, 0x6c,
0x65, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x0c, 0x3a, 0x04, 0x6e,
0x6f, 0x6e, 0x65, 0x52, 0x0b, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x42, 0x79, 0x74, 0x65, 0x73,
0x12, 0x59, 0x0a, 0x0f, 0x73, 0x74, 0x61, 0x6e, 0x64, 0x61, 0x6c, 0x6f, 0x6e, 0x65, 0x5f, 0x65,
0x6e, 0x75, 0x6d, 0x18, 0x16, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x30, 0x2e, 0x67, 0x6f, 0x6f, 0x67,
0x6c, 0x65, 0x2e, 0x65, 0x78, 0x70, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x32, 0x2e, 0x74,
0x65, 0x73, 0x74, 0x2e, 0x54, 0x65, 0x73, 0x74, 0x41, 0x6c, 0x6c, 0x54, 0x79, 0x70, 0x65, 0x73,
0x2e, 0x4e, 0x65, 0x73, 0x74, 0x65, 0x64, 0x45, 0x6e, 0x75, 0x6d, 0x52, 0x0e, 0x73, 0x74, 0x61,
0x6e, 0x64, 0x61, 0x6c, 0x6f, 0x6e, 0x65, 0x45, 0x6e, 0x75, 0x6d, 0x12, 0x53, 0x0a, 0x0b, 0x6e,
0x65, 0x73, 0x74, 0x65, 0x64, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x18, 0x17, 0x20, 0x01, 0x28, 0x0a,
0x32, 0x31, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x65, 0x78, 0x70, 0x72, 0x2e, 0x70,
0x72, 0x6f, 0x74, 0x6f, 0x32, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x54, 0x65, 0x73, 0x74, 0x41,
0x6c, 0x6c, 0x54, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x4e, 0x65, 0x73, 0x74, 0x65, 0x64, 0x47, 0x72,
0x6f, 0x75, 0x70, 0x52, 0x0b, 0x6e, 0x65, 0x73, 0x74, 0x65, 0x64, 0x67, 0x72, 0x6f, 0x75, 0x70,
0x12, 0x33, 0x0a, 0x0a, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x5f, 0x61, 0x6e, 0x79, 0x18, 0x64,
0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72,
0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x41, 0x6e, 0x79, 0x52, 0x09, 0x73, 0x69, 0x6e, 0x67,
0x6c, 0x65, 0x41, 0x6e, 0x79, 0x12, 0x42, 0x0a, 0x0f, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x5f,
0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x65, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19,
0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66,
0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0e, 0x73, 0x69, 0x6e, 0x67, 0x6c,
0x65, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x45, 0x0a, 0x10, 0x73, 0x69, 0x6e,
0x67, 0x6c, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x66, 0x20,
0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f,
0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52,
0x0f, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70,
0x12, 0x3c, 0x0a, 0x0d, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x5f, 0x73, 0x74, 0x72, 0x75, 0x63,
0x74, 0x18, 0x67, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65,
0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74,
0x52, 0x0c, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x12, 0x39,
0x0a, 0x0c, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x68,
0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72,
0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0b, 0x73, 0x69,
0x6e, 0x67, 0x6c, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x4d, 0x0a, 0x14, 0x73, 0x69, 0x6e,
0x67, 0x6c, 0x65, 0x5f, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x5f, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65,
0x72, 0x18, 0x69, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65,
0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x49, 0x6e, 0x74, 0x36, 0x34, 0x56,
0x61, 0x6c, 0x75, 0x65, 0x52, 0x12, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x49, 0x6e, 0x74, 0x36,
0x34, 0x57, 0x72, 0x61, 0x70, 0x70, 0x65, 0x72, 0x12, 0x4d, 0x0a, 0x14, 0x73, 0x69, 0x6e, 0x67,
0x6c, 0x65, 0x5f, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x72,
0x18, 0x6a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e,
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x49, 0x6e, 0x74, 0x33, 0x32, 0x56, 0x61,
0x6c, 0x75, 0x65, 0x52, 0x12, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x49, 0x6e, 0x74, 0x33, 0x32,
0x57, 0x72, 0x61, 0x70, 0x70, 0x65, 0x72, 0x12, 0x50, 0x0a, 0x15, 0x73, 0x69, 0x6e, 0x67, 0x6c,
0x65, 0x5f, 0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x5f, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x72,
0x18, 0x6b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e,
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x56,
0x61, 0x6c, 0x75, 0x65, 0x52, 0x13, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x44, 0x6f, 0x75, 0x62,
0x6c, 0x65, 0x57, 0x72, 0x61, 0x70, 0x70, 0x65, 0x72, 0x12, 0x4d, 0x0a, 0x14, 0x73, 0x69, 0x6e,
0x67, 0x6c, 0x65, 0x5f, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x5f, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65,
0x72, 0x18, 0x6c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65,
0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x46, 0x6c, 0x6f, 0x61, 0x74, 0x56,
0x61, 0x6c, 0x75, 0x65, 0x52, 0x12, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x46, 0x6c, 0x6f, 0x61,
0x74, 0x57, 0x72, 0x61, 0x70, 0x70, 0x65, 0x72, 0x12, 0x50, 0x0a, 0x15, 0x73, 0x69, 0x6e, 0x67,
0x6c, 0x65, 0x5f, 0x75, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x5f, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65,
0x72, 0x18, 0x6d, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65,
0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x55, 0x49, 0x6e, 0x74, 0x36, 0x34,
0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x13, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x55, 0x69, 0x6e,
0x74, 0x36, 0x34, 0x57, 0x72, 0x61, 0x70, 0x70, 0x65, 0x72, 0x12, 0x50, 0x0a, 0x15, 0x73, 0x69,
0x6e, 0x67, 0x6c, 0x65, 0x5f, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x77, 0x72, 0x61, 0x70,
0x70, 0x65, 0x72, 0x18, 0x6e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67,
0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x55, 0x49, 0x6e, 0x74,
0x33, 0x32, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x13, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x55,
0x69, 0x6e, 0x74, 0x33, 0x32, 0x57, 0x72, 0x61, 0x70, 0x70, 0x65, 0x72, 0x12, 0x50, 0x0a, 0x15,
0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x5f, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x5f, 0x77, 0x72,
0x61, 0x70, 0x70, 0x65, 0x72, 0x18, 0x6f, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f,
0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74,
0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x13, 0x73, 0x69, 0x6e, 0x67, 0x6c,
0x65, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x57, 0x72, 0x61, 0x70, 0x70, 0x65, 0x72, 0x12, 0x4a,
0x0a, 0x13, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x5f, 0x62, 0x6f, 0x6f, 0x6c, 0x5f, 0x77, 0x72,
0x61, 0x70, 0x70, 0x65, 0x72, 0x18, 0x70, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f,
0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x42, 0x6f,
0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x11, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x42,
0x6f, 0x6f, 0x6c, 0x57, 0x72, 0x61, 0x70, 0x70, 0x65, 0x72, 0x12, 0x4d, 0x0a, 0x14, 0x73, 0x69,
0x6e, 0x67, 0x6c, 0x65, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x5f, 0x77, 0x72, 0x61, 0x70, 0x70,
0x65, 0x72, 0x18, 0x71, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c,
0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x42, 0x79, 0x74, 0x65, 0x73,
0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x12, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x42, 0x79, 0x74,
0x65, 0x73, 0x57, 0x72, 0x61, 0x70, 0x70, 0x65, 0x72, 0x12, 0x69, 0x0a, 0x15, 0x73, 0x69, 0x6e,
0x67, 0x6c, 0x65, 0x5f, 0x6e, 0x65, 0x73, 0x74, 0x65, 0x64, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61,
0x67, 0x65, 0x18, 0x12, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x33, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c,
0x65, 0x2e, 0x65, 0x78, 0x70, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x32, 0x2e, 0x74, 0x65,
0x73, 0x74, 0x2e, 0x54, 0x65, 0x73, 0x74, 0x41, 0x6c, 0x6c, 0x54, 0x79, 0x70, 0x65, 0x73, 0x2e,
0x4e, 0x65, 0x73, 0x74, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x48, 0x00, 0x52,
0x13, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x4e, 0x65, 0x73, 0x74, 0x65, 0x64, 0x4d, 0x65, 0x73,
0x73, 0x61, 0x67, 0x65, 0x12, 0x65, 0x0a, 0x12, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x5f, 0x6e,
0x65, 0x73, 0x74, 0x65, 0x64, 0x5f, 0x65, 0x6e, 0x75, 0x6d, 0x18, 0x15, 0x20, 0x01, 0x28, 0x0e,
0x32, 0x30, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x65, 0x78, 0x70, 0x72, 0x2e, 0x70,
0x72, 0x6f, 0x74, 0x6f, 0x32, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x54, 0x65, 0x73, 0x74, 0x41,
0x6c, 0x6c, 0x54, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x4e, 0x65, 0x73, 0x74, 0x65, 0x64, 0x45, 0x6e,
0x75, 0x6d, 0x3a, 0x03, 0x42, 0x41, 0x52, 0x48, 0x00, 0x52, 0x10, 0x73, 0x69, 0x6e, 0x67, 0x6c,
0x65, 0x4e, 0x65, 0x73, 0x74, 0x65, 0x64, 0x45, 0x6e, 0x75, 0x6d, 0x12, 0x25, 0x0a, 0x0e, 0x72,
0x65, 0x70, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x18, 0x1f, 0x20,
0x03, 0x28, 0x05, 0x52, 0x0d, 0x72, 0x65, 0x70, 0x65, 0x61, 0x74, 0x65, 0x64, 0x49, 0x6e, 0x74,
0x33, 0x32, 0x12, 0x25, 0x0a, 0x0e, 0x72, 0x65, 0x70, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x69,
0x6e, 0x74, 0x36, 0x34, 0x18, 0x20, 0x20, 0x03, 0x28, 0x03, 0x52, 0x0d, 0x72, 0x65, 0x70, 0x65,
0x61, 0x74, 0x65, 0x64, 0x49, 0x6e, 0x74, 0x36, 0x34, 0x12, 0x27, 0x0a, 0x0f, 0x72, 0x65, 0x70,
0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x18, 0x21, 0x20, 0x03,
0x28, 0x0d, 0x52, 0x0e, 0x72, 0x65, 0x70, 0x65, 0x61, 0x74, 0x65, 0x64, 0x55, 0x69, 0x6e, 0x74,
0x33, 0x32, 0x12, 0x27, 0x0a, 0x0f, 0x72, 0x65, 0x70, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x75,
0x69, 0x6e, 0x74, 0x36, 0x34, 0x18, 0x22, 0x20, 0x03, 0x28, 0x04, 0x52, 0x0e, 0x72, 0x65, 0x70,
0x65, 0x61, 0x74, 0x65, 0x64, 0x55, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x12, 0x27, 0x0a, 0x0f, 0x72,
0x65, 0x70, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x73, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x18, 0x23,
0x20, 0x03, 0x28, 0x11, 0x52, 0x0e, 0x72, 0x65, 0x70, 0x65, 0x61, 0x74, 0x65, 0x64, 0x53, 0x69,
0x6e, 0x74, 0x33, 0x32, 0x12, 0x27, 0x0a, 0x0f, 0x72, 0x65, 0x70, 0x65, 0x61, 0x74, 0x65, 0x64,
0x5f, 0x73, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x18, 0x24, 0x20, 0x03, 0x28, 0x12, 0x52, 0x0e, 0x72,
0x65, 0x70, 0x65, 0x61, 0x74, 0x65, 0x64, 0x53, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x12, 0x29, 0x0a,
0x10, 0x72, 0x65, 0x70, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x66, 0x69, 0x78, 0x65, 0x64, 0x33,
0x32, 0x18, 0x25, 0x20, 0x03, 0x28, 0x07, 0x52, 0x0f, 0x72, 0x65, 0x70, 0x65, 0x61, 0x74, 0x65,
0x64, 0x46, 0x69, 0x78, 0x65, 0x64, 0x33, 0x32, 0x12, 0x29, 0x0a, 0x10, 0x72, 0x65, 0x70, 0x65,
0x61, 0x74, 0x65, 0x64, 0x5f, 0x66, 0x69, 0x78, 0x65, 0x64, 0x36, 0x34, 0x18, 0x26, 0x20, 0x03,
0x28, 0x06, 0x52, 0x0f, 0x72, 0x65, 0x70, 0x65, 0x61, 0x74, 0x65, 0x64, 0x46, 0x69, 0x78, 0x65,
0x64, 0x36, 0x34, 0x12, 0x2b, 0x0a, 0x11, 0x72, 0x65, 0x70, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f,
0x73, 0x66, 0x69, 0x78, 0x65, 0x64, 0x33, 0x32, 0x18, 0x27, 0x20, 0x03, 0x28, 0x0f, 0x52, 0x10,
0x72, 0x65, 0x70, 0x65, 0x61, 0x74, 0x65, 0x64, 0x53, 0x66, 0x69, 0x78, 0x65, 0x64, 0x33, 0x32,
0x12, 0x2b, 0x0a, 0x11, 0x72, 0x65, 0x70, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x73, 0x66, 0x69,
0x78, 0x65, 0x64, 0x36, 0x34, 0x18, 0x28, 0x20, 0x03, 0x28, 0x10, 0x52, 0x10, 0x72, 0x65, 0x70,
0x65, 0x61, 0x74, 0x65, 0x64, 0x53, 0x66, 0x69, 0x78, 0x65, 0x64, 0x36, 0x34, 0x12, 0x25, 0x0a,
0x0e, 0x72, 0x65, 0x70, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x18,
0x29, 0x20, 0x03, 0x28, 0x02, 0x52, 0x0d, 0x72, 0x65, 0x70, 0x65, 0x61, 0x74, 0x65, 0x64, 0x46,
0x6c, 0x6f, 0x61, 0x74, 0x12, 0x27, 0x0a, 0x0f, 0x72, 0x65, 0x70, 0x65, 0x61, 0x74, 0x65, 0x64,
0x5f, 0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x18, 0x2a, 0x20, 0x03, 0x28, 0x01, 0x52, 0x0e, 0x72,
0x65, 0x70, 0x65, 0x61, 0x74, 0x65, 0x64, 0x44, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x12, 0x23, 0x0a,
0x0d, 0x72, 0x65, 0x70, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x62, 0x6f, 0x6f, 0x6c, 0x18, 0x2b,
0x20, 0x03, 0x28, 0x08, 0x52, 0x0c, 0x72, 0x65, 0x70, 0x65, 0x61, 0x74, 0x65, 0x64, 0x42, 0x6f,
0x6f, 0x6c, 0x12, 0x27, 0x0a, 0x0f, 0x72, 0x65, 0x70, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x73,
0x74, 0x72, 0x69, 0x6e, 0x67, 0x18, 0x2c, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0e, 0x72, 0x65, 0x70,
0x65, 0x61, 0x74, 0x65, 0x64, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x12, 0x25, 0x0a, 0x0e, 0x72,
0x65, 0x70, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x2d, 0x20,
0x03, 0x28, 0x0c, 0x52, 0x0d, 0x72, 0x65, 0x70, 0x65, 0x61, 0x74, 0x65, 0x64, 0x42, 0x79, 0x74,
0x65, 0x73, 0x12, 0x6b, 0x0a, 0x17, 0x72, 0x65, 0x70, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x6e,
0x65, 0x73, 0x74, 0x65, 0x64, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x30, 0x20,
0x03, 0x28, 0x0b, 0x32, 0x33, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x65, 0x78, 0x70,
0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x32, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x54, 0x65,
0x73, 0x74, 0x41, 0x6c, 0x6c, 0x54, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x4e, 0x65, 0x73, 0x74, 0x65,
0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x15, 0x72, 0x65, 0x70, 0x65, 0x61, 0x74,
0x65, 0x64, 0x4e, 0x65, 0x73, 0x74, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12,
0x62, 0x0a, 0x14, 0x72, 0x65, 0x70, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x6e, 0x65, 0x73, 0x74,
0x65, 0x64, 0x5f, 0x65, 0x6e, 0x75, 0x6d, 0x18, 0x33, 0x20, 0x03, 0x28, 0x0e, 0x32, 0x30, 0x2e,
0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x65, 0x78, 0x70, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74,
0x6f, 0x32, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x54, 0x65, 0x73, 0x74, 0x41, 0x6c, 0x6c, 0x54,
0x79, 0x70, 0x65, 0x73, 0x2e, 0x4e, 0x65, 0x73, 0x74, 0x65, 0x64, 0x45, 0x6e, 0x75, 0x6d, 0x52,
0x12, 0x72, 0x65, 0x70, 0x65, 0x61, 0x74, 0x65, 0x64, 0x4e, 0x65, 0x73, 0x74, 0x65, 0x64, 0x45,
0x6e, 0x75, 0x6d, 0x12, 0x36, 0x0a, 0x15, 0x72, 0x65, 0x70, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f,
0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x5f, 0x70, 0x69, 0x65, 0x63, 0x65, 0x18, 0x36, 0x20, 0x03,
0x28, 0x09, 0x42, 0x02, 0x08, 0x02, 0x52, 0x13, 0x72, 0x65, 0x70, 0x65, 0x61, 0x74, 0x65, 0x64,
0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x50, 0x69, 0x65, 0x63, 0x65, 0x12, 0x27, 0x0a, 0x0d, 0x72,
0x65, 0x70, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x63, 0x6f, 0x72, 0x64, 0x18, 0x37, 0x20, 0x03,
0x28, 0x09, 0x42, 0x02, 0x08, 0x01, 0x52, 0x0c, 0x72, 0x65, 0x70, 0x65, 0x61, 0x74, 0x65, 0x64,
0x43, 0x6f, 0x72, 0x64, 0x12, 0x67, 0x0a, 0x15, 0x72, 0x65, 0x70, 0x65, 0x61, 0x74, 0x65, 0x64,
0x5f, 0x6c, 0x61, 0x7a, 0x79, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x39, 0x20,
0x03, 0x28, 0x0b, 0x32, 0x33, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x65, 0x78, 0x70,
0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x32, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x54, 0x65,
0x73, 0x74, 0x41, 0x6c, 0x6c, 0x54, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x4e, 0x65, 0x73, 0x74, 0x65,
0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x13, 0x72, 0x65, 0x70, 0x65, 0x61, 0x74,
0x65, 0x64, 0x4c, 0x61, 0x7a, 0x79, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x66, 0x0a,
0x11, 0x6d, 0x61, 0x70, 0x5f, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x5f, 0x73, 0x74, 0x72, 0x69,
0x6e, 0x67, 0x18, 0x3a, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x3a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c,
0x65, 0x2e, 0x65, 0x78, 0x70, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x32, 0x2e, 0x74, 0x65,
0x73, 0x74, 0x2e, 0x54, 0x65, 0x73, 0x74, 0x41, 0x6c, 0x6c, 0x54, 0x79, 0x70, 0x65, 0x73, 0x2e,
0x4d, 0x61, 0x70, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x45,
0x6e, 0x74, 0x72, 0x79, 0x52, 0x0f, 0x6d, 0x61, 0x70, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x53,
0x74, 0x72, 0x69, 0x6e, 0x67, 0x12, 0x70, 0x0a, 0x15, 0x6d, 0x61, 0x70, 0x5f, 0x69, 0x6e, 0x74,
0x36, 0x34, 0x5f, 0x6e, 0x65, 0x73, 0x74, 0x65, 0x64, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x3b,
0x20, 0x03, 0x28, 0x0b, 0x32, 0x3d, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x65, 0x78,
0x70, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x32, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x54,
0x65, 0x73, 0x74, 0x41, 0x6c, 0x6c, 0x54, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x4d, 0x61, 0x70, 0x49,
0x6e, 0x74, 0x36, 0x34, 0x4e, 0x65, 0x73, 0x74, 0x65, 0x64, 0x54, 0x79, 0x70, 0x65, 0x45, 0x6e,
0x74, 0x72, 0x79, 0x52, 0x12, 0x6d, 0x61, 0x70, 0x49, 0x6e, 0x74, 0x36, 0x34, 0x4e, 0x65, 0x73,
0x74, 0x65, 0x64, 0x54, 0x79, 0x70, 0x65, 0x1a, 0x1f, 0x0a, 0x0d, 0x4e, 0x65, 0x73, 0x74, 0x65,
0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x62, 0x62, 0x18, 0x01,
0x20, 0x01, 0x28, 0x05, 0x52, 0x02, 0x62, 0x62, 0x1a, 0x4b, 0x0a, 0x0b, 0x4e, 0x65, 0x73, 0x74,
0x65, 0x64, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x12, 0x1b, 0x0a, 0x09, 0x6e, 0x65, 0x73, 0x74, 0x65,
0x64, 0x5f, 0x69, 0x64, 0x18, 0x18, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x6e, 0x65, 0x73, 0x74,
0x65, 0x64, 0x49, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x6e, 0x65, 0x73, 0x74, 0x65, 0x64, 0x5f, 0x6e,
0x61, 0x6d, 0x65, 0x18, 0x19, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x6e, 0x65, 0x73, 0x74, 0x65,
0x64, 0x4e, 0x61, 0x6d, 0x65, 0x1a, 0x42, 0x0a, 0x14, 0x4d, 0x61, 0x70, 0x53, 0x74, 0x72, 0x69,
0x6e, 0x67, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a,
0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12,
0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05,
0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x72, 0x0a, 0x17, 0x4d, 0x61, 0x70,
0x49, 0x6e, 0x74, 0x36, 0x34, 0x4e, 0x65, 0x73, 0x74, 0x65, 0x64, 0x54, 0x79, 0x70, 0x65, 0x45,
0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28,
0x03, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x41, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18,
0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x65,
0x78, 0x70, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x32, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e,
0x4e, 0x65, 0x73, 0x74, 0x65, 0x64, 0x54, 0x65, 0x73, 0x74, 0x41, 0x6c, 0x6c, 0x54, 0x79, 0x70,
0x65, 0x73, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x27, 0x0a,
0x0a, 0x4e, 0x65, 0x73, 0x74, 0x65, 0x64, 0x45, 0x6e, 0x75, 0x6d, 0x12, 0x07, 0x0a, 0x03, 0x46,
0x4f, 0x4f, 0x10, 0x00, 0x12, 0x07, 0x0a, 0x03, 0x42, 0x41, 0x52, 0x10, 0x01, 0x12, 0x07, 0x0a,
0x03, 0x42, 0x41, 0x5a, 0x10, 0x02, 0x42, 0x0d, 0x0a, 0x0b, 0x6e, 0x65, 0x73, 0x74, 0x65, 0x64,
0x5f, 0x74, 0x79, 0x70, 0x65, 0x22, 0x98, 0x01, 0x0a, 0x12, 0x4e, 0x65, 0x73, 0x74, 0x65, 0x64,
0x54, 0x65, 0x73, 0x74, 0x41, 0x6c, 0x6c, 0x54, 0x79, 0x70, 0x65, 0x73, 0x12, 0x41, 0x0a, 0x05,
0x63, 0x68, 0x69, 0x6c, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x67, 0x6f,
0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x65, 0x78, 0x70, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x32,
0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x4e, 0x65, 0x73, 0x74, 0x65, 0x64, 0x54, 0x65, 0x73, 0x74,
0x41, 0x6c, 0x6c, 0x54, 0x79, 0x70, 0x65, 0x73, 0x52, 0x05, 0x63, 0x68, 0x69, 0x6c, 0x64, 0x12,
0x3f, 0x0a, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b,
0x32, 0x25, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x65, 0x78, 0x70, 0x72, 0x2e, 0x70,
0x72, 0x6f, 0x74, 0x6f, 0x32, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x54, 0x65, 0x73, 0x74, 0x41,
0x6c, 0x6c, 0x54, 0x79, 0x70, 0x65, 0x73, 0x52, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64,
0x22, 0x3b, 0x0a, 0x0b, 0x45, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12,
0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e,
0x61, 0x6d, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52,
0x02, 0x69, 0x6e, 0x2a, 0x08, 0x08, 0x64, 0x10, 0x80, 0x80, 0x80, 0x80, 0x02, 0x22, 0xce, 0x01,
0x0a, 0x13, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x64, 0x65, 0x64, 0x45, 0x78, 0x61, 0x6d, 0x70, 0x6c,
0x65, 0x54, 0x79, 0x70, 0x65, 0x32, 0x51, 0x0a, 0x11, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x64, 0x65,
0x64, 0x5f, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x73, 0x12, 0x24, 0x2e, 0x67, 0x6f, 0x6f,
0x67, 0x6c, 0x65, 0x2e, 0x65, 0x78, 0x70, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x32, 0x2e,
0x74, 0x65, 0x73, 0x74, 0x2e, 0x45, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x54, 0x79, 0x70, 0x65,
0x18, 0x67, 0x20, 0x03, 0x28, 0x09, 0x52, 0x10, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x64, 0x65, 0x64,
0x45, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x73, 0x32, 0x64, 0x0a, 0x08, 0x65, 0x6e, 0x75, 0x6d,
0x5f, 0x65, 0x78, 0x74, 0x12, 0x24, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x65, 0x78,
0x70, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x32, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x45,
0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x54, 0x79, 0x70, 0x65, 0x18, 0x68, 0x20, 0x01, 0x28, 0x0e,
0x32, 0x23, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x65, 0x78, 0x70, 0x72, 0x2e, 0x70,
0x72, 0x6f, 0x74, 0x6f, 0x32, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x47, 0x6c, 0x6f, 0x62, 0x61,
0x6c, 0x45, 0x6e, 0x75, 0x6d, 0x52, 0x07, 0x65, 0x6e, 0x75, 0x6d, 0x45, 0x78, 0x74, 0x2a, 0x27,
0x0a, 0x0a, 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x45, 0x6e, 0x75, 0x6d, 0x12, 0x07, 0x0a, 0x03,
0x47, 0x4f, 0x4f, 0x10, 0x00, 0x12, 0x07, 0x0a, 0x03, 0x47, 0x41, 0x52, 0x10, 0x01, 0x12, 0x07,
0x0a, 0x03, 0x47, 0x41, 0x5a, 0x10, 0x02, 0x42, 0x28, 0x5a, 0x26, 0x67, 0x69, 0x74, 0x68, 0x75,
0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x63, 0x65, 0x6c,
0x2d, 0x67, 0x6f, 0x2f, 0x74, 0x65, 0x73, 0x74, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x32, 0x70,
0x62,
}
var (
file_test_proto2pb_test_all_types_proto_rawDescOnce sync.Once
file_test_proto2pb_test_all_types_proto_rawDescData = file_test_proto2pb_test_all_types_proto_rawDesc
)
func file_test_proto2pb_test_all_types_proto_rawDescGZIP() []byte {
file_test_proto2pb_test_all_types_proto_rawDescOnce.Do(func() {
file_test_proto2pb_test_all_types_proto_rawDescData = protoimpl.X.CompressGZIP(file_test_proto2pb_test_all_types_proto_rawDescData)
})
return file_test_proto2pb_test_all_types_proto_rawDescData
}
var file_test_proto2pb_test_all_types_proto_enumTypes = make([]protoimpl.EnumInfo, 2)
var file_test_proto2pb_test_all_types_proto_msgTypes = make([]protoimpl.MessageInfo, 8)
var file_test_proto2pb_test_all_types_proto_goTypes = []interface{}{
(GlobalEnum)(0), // 0: google.expr.proto2.test.GlobalEnum
(TestAllTypes_NestedEnum)(0), // 1: google.expr.proto2.test.TestAllTypes.NestedEnum
(*TestAllTypes)(nil), // 2: google.expr.proto2.test.TestAllTypes
(*NestedTestAllTypes)(nil), // 3: google.expr.proto2.test.NestedTestAllTypes
(*ExampleType)(nil), // 4: google.expr.proto2.test.ExampleType
(*ExtendedExampleType)(nil), // 5: google.expr.proto2.test.ExtendedExampleType
(*TestAllTypes_NestedMessage)(nil), // 6: google.expr.proto2.test.TestAllTypes.NestedMessage
(*TestAllTypes_NestedGroup)(nil), // 7: google.expr.proto2.test.TestAllTypes.NestedGroup
nil, // 8: google.expr.proto2.test.TestAllTypes.MapStringStringEntry
nil, // 9: google.expr.proto2.test.TestAllTypes.MapInt64NestedTypeEntry
(*anypb.Any)(nil), // 10: google.protobuf.Any
(*durationpb.Duration)(nil), // 11: google.protobuf.Duration
(*timestamppb.Timestamp)(nil), // 12: google.protobuf.Timestamp
(*structpb.Struct)(nil), // 13: google.protobuf.Struct
(*structpb.Value)(nil), // 14: google.protobuf.Value
(*wrapperspb.Int64Value)(nil), // 15: google.protobuf.Int64Value
(*wrapperspb.Int32Value)(nil), // 16: google.protobuf.Int32Value
(*wrapperspb.DoubleValue)(nil), // 17: google.protobuf.DoubleValue
(*wrapperspb.FloatValue)(nil), // 18: google.protobuf.FloatValue
(*wrapperspb.UInt64Value)(nil), // 19: google.protobuf.UInt64Value
(*wrapperspb.UInt32Value)(nil), // 20: google.protobuf.UInt32Value
(*wrapperspb.StringValue)(nil), // 21: google.protobuf.StringValue
(*wrapperspb.BoolValue)(nil), // 22: google.protobuf.BoolValue
(*wrapperspb.BytesValue)(nil), // 23: google.protobuf.BytesValue
}
var file_test_proto2pb_test_all_types_proto_depIdxs = []int32{
1, // 0: google.expr.proto2.test.TestAllTypes.standalone_enum:type_name -> google.expr.proto2.test.TestAllTypes.NestedEnum
7, // 1: google.expr.proto2.test.TestAllTypes.nestedgroup:type_name -> google.expr.proto2.test.TestAllTypes.NestedGroup
10, // 2: google.expr.proto2.test.TestAllTypes.single_any:type_name -> google.protobuf.Any
11, // 3: google.expr.proto2.test.TestAllTypes.single_duration:type_name -> google.protobuf.Duration
12, // 4: google.expr.proto2.test.TestAllTypes.single_timestamp:type_name -> google.protobuf.Timestamp
13, // 5: google.expr.proto2.test.TestAllTypes.single_struct:type_name -> google.protobuf.Struct
14, // 6: google.expr.proto2.test.TestAllTypes.single_value:type_name -> google.protobuf.Value
15, // 7: google.expr.proto2.test.TestAllTypes.single_int64_wrapper:type_name -> google.protobuf.Int64Value
16, // 8: google.expr.proto2.test.TestAllTypes.single_int32_wrapper:type_name -> google.protobuf.Int32Value
17, // 9: google.expr.proto2.test.TestAllTypes.single_double_wrapper:type_name -> google.protobuf.DoubleValue
18, // 10: google.expr.proto2.test.TestAllTypes.single_float_wrapper:type_name -> google.protobuf.FloatValue
19, // 11: google.expr.proto2.test.TestAllTypes.single_uint64_wrapper:type_name -> google.protobuf.UInt64Value
20, // 12: google.expr.proto2.test.TestAllTypes.single_uint32_wrapper:type_name -> google.protobuf.UInt32Value
21, // 13: google.expr.proto2.test.TestAllTypes.single_string_wrapper:type_name -> google.protobuf.StringValue
22, // 14: google.expr.proto2.test.TestAllTypes.single_bool_wrapper:type_name -> google.protobuf.BoolValue
23, // 15: google.expr.proto2.test.TestAllTypes.single_bytes_wrapper:type_name -> google.protobuf.BytesValue
6, // 16: google.expr.proto2.test.TestAllTypes.single_nested_message:type_name -> google.expr.proto2.test.TestAllTypes.NestedMessage
1, // 17: google.expr.proto2.test.TestAllTypes.single_nested_enum:type_name -> google.expr.proto2.test.TestAllTypes.NestedEnum
6, // 18: google.expr.proto2.test.TestAllTypes.repeated_nested_message:type_name -> google.expr.proto2.test.TestAllTypes.NestedMessage
1, // 19: google.expr.proto2.test.TestAllTypes.repeated_nested_enum:type_name -> google.expr.proto2.test.TestAllTypes.NestedEnum
6, // 20: google.expr.proto2.test.TestAllTypes.repeated_lazy_message:type_name -> google.expr.proto2.test.TestAllTypes.NestedMessage
8, // 21: google.expr.proto2.test.TestAllTypes.map_string_string:type_name -> google.expr.proto2.test.TestAllTypes.MapStringStringEntry
9, // 22: google.expr.proto2.test.TestAllTypes.map_int64_nested_type:type_name -> google.expr.proto2.test.TestAllTypes.MapInt64NestedTypeEntry
3, // 23: google.expr.proto2.test.NestedTestAllTypes.child:type_name -> google.expr.proto2.test.NestedTestAllTypes
2, // 24: google.expr.proto2.test.NestedTestAllTypes.payload:type_name -> google.expr.proto2.test.TestAllTypes
3, // 25: google.expr.proto2.test.TestAllTypes.MapInt64NestedTypeEntry.value:type_name -> google.expr.proto2.test.NestedTestAllTypes
4, // 26: google.expr.proto2.test.ExtendedExampleType.extended_examples:extendee -> google.expr.proto2.test.ExampleType
4, // 27: google.expr.proto2.test.ExtendedExampleType.enum_ext:extendee -> google.expr.proto2.test.ExampleType
0, // 28: google.expr.proto2.test.ExtendedExampleType.enum_ext:type_name -> google.expr.proto2.test.GlobalEnum
29, // [29:29] is the sub-list for method output_type
29, // [29:29] is the sub-list for method input_type
28, // [28:29] is the sub-list for extension type_name
26, // [26:28] is the sub-list for extension extendee
0, // [0:26] is the sub-list for field type_name
}
func init() { file_test_proto2pb_test_all_types_proto_init() }
func file_test_proto2pb_test_all_types_proto_init() {
if File_test_proto2pb_test_all_types_proto != nil {
return
}
if !protoimpl.UnsafeEnabled {
file_test_proto2pb_test_all_types_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*TestAllTypes); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_test_proto2pb_test_all_types_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*NestedTestAllTypes); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_test_proto2pb_test_all_types_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*ExampleType); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
case 3:
return &v.extensionFields
default:
return nil
}
}
file_test_proto2pb_test_all_types_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*ExtendedExampleType); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_test_proto2pb_test_all_types_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*TestAllTypes_NestedMessage); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_test_proto2pb_test_all_types_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*TestAllTypes_NestedGroup); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
}
file_test_proto2pb_test_all_types_proto_msgTypes[0].OneofWrappers = []interface{}{
(*TestAllTypes_SingleNestedMessage)(nil),
(*TestAllTypes_SingleNestedEnum)(nil),
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_test_proto2pb_test_all_types_proto_rawDesc,
NumEnums: 2,
NumMessages: 8,
NumExtensions: 2,
NumServices: 0,
},
GoTypes: file_test_proto2pb_test_all_types_proto_goTypes,
DependencyIndexes: file_test_proto2pb_test_all_types_proto_depIdxs,
EnumInfos: file_test_proto2pb_test_all_types_proto_enumTypes,
MessageInfos: file_test_proto2pb_test_all_types_proto_msgTypes,
ExtensionInfos: file_test_proto2pb_test_all_types_proto_extTypes,
}.Build()
File_test_proto2pb_test_all_types_proto = out.File
file_test_proto2pb_test_all_types_proto_rawDesc = nil
file_test_proto2pb_test_all_types_proto_goTypes = nil
file_test_proto2pb_test_all_types_proto_depIdxs = nil
}
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.28.1
// protoc v3.21.5
// source: test/proto2pb/test_extensions.proto
package proto2pb
import (
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
wrapperspb "google.golang.org/protobuf/types/known/wrapperspb"
reflect "reflect"
sync "sync"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
type ExternalMessageType struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
}
func (x *ExternalMessageType) Reset() {
*x = ExternalMessageType{}
if protoimpl.UnsafeEnabled {
mi := &file_test_proto2pb_test_extensions_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *ExternalMessageType) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*ExternalMessageType) ProtoMessage() {}
func (x *ExternalMessageType) ProtoReflect() protoreflect.Message {
mi := &file_test_proto2pb_test_extensions_proto_msgTypes[0]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use ExternalMessageType.ProtoReflect.Descriptor instead.
func (*ExternalMessageType) Descriptor() ([]byte, []int) {
return file_test_proto2pb_test_extensions_proto_rawDescGZIP(), []int{0}
}
var file_test_proto2pb_test_extensions_proto_extTypes = []protoimpl.ExtensionInfo{
{
ExtendedType: (*ExampleType)(nil),
ExtensionType: (*ExampleType)(nil),
Field: 100,
Name: "google.expr.proto2.test.nested_example",
Tag: "bytes,100,opt,name=nested_example",
Filename: "test/proto2pb/test_extensions.proto",
},
{
ExtendedType: (*ExampleType)(nil),
ExtensionType: (*int32)(nil),
Field: 101,
Name: "google.expr.proto2.test.int32_ext",
Tag: "varint,101,opt,name=int32_ext",
Filename: "test/proto2pb/test_extensions.proto",
},
{
ExtendedType: (*ExampleType)(nil),
ExtensionType: (*wrapperspb.Int32Value)(nil),
Field: 102,
Name: "google.expr.proto2.test.int32_wrapper_ext",
Tag: "bytes,102,opt,name=int32_wrapper_ext",
Filename: "test/proto2pb/test_extensions.proto",
},
{
ExtendedType: (*ExampleType)(nil),
ExtensionType: (*int64)(nil),
Field: 201,
Name: "google.expr.proto2.test.ExternalMessageType.int64_ext",
Tag: "varint,201,opt,name=int64_ext",
Filename: "test/proto2pb/test_extensions.proto",
},
}
// Extension fields to ExampleType.
var (
// optional google.expr.proto2.test.ExampleType nested_example = 100;
E_NestedExample = &file_test_proto2pb_test_extensions_proto_extTypes[0]
// optional int32 int32_ext = 101;
E_Int32Ext = &file_test_proto2pb_test_extensions_proto_extTypes[1]
// optional google.protobuf.Int32Value int32_wrapper_ext = 102;
E_Int32WrapperExt = &file_test_proto2pb_test_extensions_proto_extTypes[2]
// optional int64 int64_ext = 201;
E_ExternalMessageType_Int64Ext = &file_test_proto2pb_test_extensions_proto_extTypes[3]
)
var File_test_proto2pb_test_extensions_proto protoreflect.FileDescriptor
var file_test_proto2pb_test_extensions_proto_rawDesc = []byte{
0x0a, 0x23, 0x74, 0x65, 0x73, 0x74, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x32, 0x70, 0x62, 0x2f,
0x74, 0x65, 0x73, 0x74, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2e,
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x17, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x65, 0x78,
0x70, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x32, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x1a, 0x1e,
0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f,
0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x72, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x22,
0x74, 0x65, 0x73, 0x74, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x32, 0x70, 0x62, 0x2f, 0x74, 0x65,
0x73, 0x74, 0x5f, 0x61, 0x6c, 0x6c, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x70, 0x72, 0x6f,
0x74, 0x6f, 0x22, 0x59, 0x0a, 0x13, 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x4d, 0x65,
0x73, 0x73, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x32, 0x42, 0x0a, 0x09, 0x69, 0x6e, 0x74,
0x36, 0x34, 0x5f, 0x65, 0x78, 0x74, 0x12, 0x24, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e,
0x65, 0x78, 0x70, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x32, 0x2e, 0x74, 0x65, 0x73, 0x74,
0x2e, 0x45, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x54, 0x79, 0x70, 0x65, 0x18, 0xc9, 0x01, 0x20,
0x01, 0x28, 0x03, 0x52, 0x08, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x45, 0x78, 0x74, 0x3a, 0x71, 0x0a,
0x0e, 0x6e, 0x65, 0x73, 0x74, 0x65, 0x64, 0x5f, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x12,
0x24, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x65, 0x78, 0x70, 0x72, 0x2e, 0x70, 0x72,
0x6f, 0x74, 0x6f, 0x32, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x45, 0x78, 0x61, 0x6d, 0x70, 0x6c,
0x65, 0x54, 0x79, 0x70, 0x65, 0x18, 0x64, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x67, 0x6f,
0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x65, 0x78, 0x70, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x32,
0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x45, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x54, 0x79, 0x70,
0x65, 0x52, 0x0d, 0x6e, 0x65, 0x73, 0x74, 0x65, 0x64, 0x45, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65,
0x3a, 0x41, 0x0a, 0x09, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x65, 0x78, 0x74, 0x12, 0x24, 0x2e,
0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x65, 0x78, 0x70, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74,
0x6f, 0x32, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x45, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x54,
0x79, 0x70, 0x65, 0x18, 0x65, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x69, 0x6e, 0x74, 0x33, 0x32,
0x45, 0x78, 0x74, 0x3a, 0x6d, 0x0a, 0x11, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x77, 0x72, 0x61,
0x70, 0x70, 0x65, 0x72, 0x5f, 0x65, 0x78, 0x74, 0x12, 0x24, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c,
0x65, 0x2e, 0x65, 0x78, 0x70, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x32, 0x2e, 0x74, 0x65,
0x73, 0x74, 0x2e, 0x45, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x54, 0x79, 0x70, 0x65, 0x18, 0x66,
0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72,
0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x49, 0x6e, 0x74, 0x33, 0x32, 0x56, 0x61, 0x6c, 0x75,
0x65, 0x52, 0x0f, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x57, 0x72, 0x61, 0x70, 0x70, 0x65, 0x72, 0x45,
0x78, 0x74, 0x42, 0x28, 0x5a, 0x26, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d,
0x2f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x63, 0x65, 0x6c, 0x2d, 0x67, 0x6f, 0x2f, 0x74,
0x65, 0x73, 0x74, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x32, 0x70, 0x62,
}
var (
file_test_proto2pb_test_extensions_proto_rawDescOnce sync.Once
file_test_proto2pb_test_extensions_proto_rawDescData = file_test_proto2pb_test_extensions_proto_rawDesc
)
func file_test_proto2pb_test_extensions_proto_rawDescGZIP() []byte {
file_test_proto2pb_test_extensions_proto_rawDescOnce.Do(func() {
file_test_proto2pb_test_extensions_proto_rawDescData = protoimpl.X.CompressGZIP(file_test_proto2pb_test_extensions_proto_rawDescData)
})
return file_test_proto2pb_test_extensions_proto_rawDescData
}
var file_test_proto2pb_test_extensions_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
var file_test_proto2pb_test_extensions_proto_goTypes = []interface{}{
(*ExternalMessageType)(nil), // 0: google.expr.proto2.test.ExternalMessageType
(*ExampleType)(nil), // 1: google.expr.proto2.test.ExampleType
(*wrapperspb.Int32Value)(nil), // 2: google.protobuf.Int32Value
}
var file_test_proto2pb_test_extensions_proto_depIdxs = []int32{
1, // 0: google.expr.proto2.test.nested_example:extendee -> google.expr.proto2.test.ExampleType
1, // 1: google.expr.proto2.test.int32_ext:extendee -> google.expr.proto2.test.ExampleType
1, // 2: google.expr.proto2.test.int32_wrapper_ext:extendee -> google.expr.proto2.test.ExampleType
1, // 3: google.expr.proto2.test.ExternalMessageType.int64_ext:extendee -> google.expr.proto2.test.ExampleType
1, // 4: google.expr.proto2.test.nested_example:type_name -> google.expr.proto2.test.ExampleType
2, // 5: google.expr.proto2.test.int32_wrapper_ext:type_name -> google.protobuf.Int32Value
6, // [6:6] is the sub-list for method output_type
6, // [6:6] is the sub-list for method input_type
4, // [4:6] is the sub-list for extension type_name
0, // [0:4] is the sub-list for extension extendee
0, // [0:0] is the sub-list for field type_name
}
func init() { file_test_proto2pb_test_extensions_proto_init() }
func file_test_proto2pb_test_extensions_proto_init() {
if File_test_proto2pb_test_extensions_proto != nil {
return
}
file_test_proto2pb_test_all_types_proto_init()
if !protoimpl.UnsafeEnabled {
file_test_proto2pb_test_extensions_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*ExternalMessageType); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_test_proto2pb_test_extensions_proto_rawDesc,
NumEnums: 0,
NumMessages: 1,
NumExtensions: 4,
NumServices: 0,
},
GoTypes: file_test_proto2pb_test_extensions_proto_goTypes,
DependencyIndexes: file_test_proto2pb_test_extensions_proto_depIdxs,
MessageInfos: file_test_proto2pb_test_extensions_proto_msgTypes,
ExtensionInfos: file_test_proto2pb_test_extensions_proto_extTypes,
}.Build()
File_test_proto2pb_test_extensions_proto = out.File
file_test_proto2pb_test_extensions_proto_rawDesc = nil
file_test_proto2pb_test_extensions_proto_goTypes = nil
file_test_proto2pb_test_extensions_proto_depIdxs = nil
}
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.28.1
// protoc v3.21.5
// source: test/proto3pb/test_all_types.proto
package proto3pb
import (
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
anypb "google.golang.org/protobuf/types/known/anypb"
durationpb "google.golang.org/protobuf/types/known/durationpb"
structpb "google.golang.org/protobuf/types/known/structpb"
timestamppb "google.golang.org/protobuf/types/known/timestamppb"
wrapperspb "google.golang.org/protobuf/types/known/wrapperspb"
reflect "reflect"
sync "sync"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
type GlobalEnum int32
const (
GlobalEnum_GOO GlobalEnum = 0
GlobalEnum_GAR GlobalEnum = 1
GlobalEnum_GAZ GlobalEnum = 2
)
// Enum value maps for GlobalEnum.
var (
GlobalEnum_name = map[int32]string{
0: "GOO",
1: "GAR",
2: "GAZ",
}
GlobalEnum_value = map[string]int32{
"GOO": 0,
"GAR": 1,
"GAZ": 2,
}
)
func (x GlobalEnum) Enum() *GlobalEnum {
p := new(GlobalEnum)
*p = x
return p
}
func (x GlobalEnum) String() string {
return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
}
func (GlobalEnum) Descriptor() protoreflect.EnumDescriptor {
return file_test_proto3pb_test_all_types_proto_enumTypes[0].Descriptor()
}
func (GlobalEnum) Type() protoreflect.EnumType {
return &file_test_proto3pb_test_all_types_proto_enumTypes[0]
}
func (x GlobalEnum) Number() protoreflect.EnumNumber {
return protoreflect.EnumNumber(x)
}
// Deprecated: Use GlobalEnum.Descriptor instead.
func (GlobalEnum) EnumDescriptor() ([]byte, []int) {
return file_test_proto3pb_test_all_types_proto_rawDescGZIP(), []int{0}
}
type TestAllTypes_NestedEnum int32
const (
TestAllTypes_FOO TestAllTypes_NestedEnum = 0
TestAllTypes_BAR TestAllTypes_NestedEnum = 1
TestAllTypes_BAZ TestAllTypes_NestedEnum = 2
)
// Enum value maps for TestAllTypes_NestedEnum.
var (
TestAllTypes_NestedEnum_name = map[int32]string{
0: "FOO",
1: "BAR",
2: "BAZ",
}
TestAllTypes_NestedEnum_value = map[string]int32{
"FOO": 0,
"BAR": 1,
"BAZ": 2,
}
)
func (x TestAllTypes_NestedEnum) Enum() *TestAllTypes_NestedEnum {
p := new(TestAllTypes_NestedEnum)
*p = x
return p
}
func (x TestAllTypes_NestedEnum) String() string {
return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
}
func (TestAllTypes_NestedEnum) Descriptor() protoreflect.EnumDescriptor {
return file_test_proto3pb_test_all_types_proto_enumTypes[1].Descriptor()
}
func (TestAllTypes_NestedEnum) Type() protoreflect.EnumType {
return &file_test_proto3pb_test_all_types_proto_enumTypes[1]
}
func (x TestAllTypes_NestedEnum) Number() protoreflect.EnumNumber {
return protoreflect.EnumNumber(x)
}
// Deprecated: Use TestAllTypes_NestedEnum.Descriptor instead.
func (TestAllTypes_NestedEnum) EnumDescriptor() ([]byte, []int) {
return file_test_proto3pb_test_all_types_proto_rawDescGZIP(), []int{0, 0}
}
type TestAllTypes struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
SingleInt32 int32 `protobuf:"varint,1,opt,name=single_int32,json=singleInt32,proto3" json:"single_int32,omitempty"`
SingleInt64 int64 `protobuf:"varint,2,opt,name=single_int64,json=singleInt64,proto3" json:"single_int64,omitempty"`
SingleUint32 uint32 `protobuf:"varint,3,opt,name=single_uint32,json=singleUint32,proto3" json:"single_uint32,omitempty"`
SingleUint64 uint64 `protobuf:"varint,4,opt,name=single_uint64,json=singleUint64,proto3" json:"single_uint64,omitempty"`
SingleSint32 int32 `protobuf:"zigzag32,5,opt,name=single_sint32,json=singleSint32,proto3" json:"single_sint32,omitempty"`
SingleSint64 int64 `protobuf:"zigzag64,6,opt,name=single_sint64,json=singleSint64,proto3" json:"single_sint64,omitempty"`
SingleFixed32 uint32 `protobuf:"fixed32,7,opt,name=single_fixed32,json=singleFixed32,proto3" json:"single_fixed32,omitempty"`
SingleFixed64 uint64 `protobuf:"fixed64,8,opt,name=single_fixed64,json=singleFixed64,proto3" json:"single_fixed64,omitempty"`
SingleSfixed32 int32 `protobuf:"fixed32,9,opt,name=single_sfixed32,json=singleSfixed32,proto3" json:"single_sfixed32,omitempty"`
SingleSfixed64 int64 `protobuf:"fixed64,10,opt,name=single_sfixed64,json=singleSfixed64,proto3" json:"single_sfixed64,omitempty"`
SingleFloat float32 `protobuf:"fixed32,11,opt,name=single_float,json=singleFloat,proto3" json:"single_float,omitempty"`
SingleDouble float64 `protobuf:"fixed64,12,opt,name=single_double,json=singleDouble,proto3" json:"single_double,omitempty"`
SingleBool bool `protobuf:"varint,13,opt,name=single_bool,json=singleBool,proto3" json:"single_bool,omitempty"`
SingleString string `protobuf:"bytes,14,opt,name=single_string,json=singleString,proto3" json:"single_string,omitempty"`
SingleBytes []byte `protobuf:"bytes,15,opt,name=single_bytes,json=singleBytes,proto3" json:"single_bytes,omitempty"`
StandaloneEnum TestAllTypes_NestedEnum `protobuf:"varint,22,opt,name=standalone_enum,json=standaloneEnum,proto3,enum=google.expr.proto3.test.TestAllTypes_NestedEnum" json:"standalone_enum,omitempty"`
SingleAny *anypb.Any `protobuf:"bytes,100,opt,name=single_any,json=singleAny,proto3" json:"single_any,omitempty"`
SingleDuration *durationpb.Duration `protobuf:"bytes,101,opt,name=single_duration,json=singleDuration,proto3" json:"single_duration,omitempty"`
SingleTimestamp *timestamppb.Timestamp `protobuf:"bytes,102,opt,name=single_timestamp,json=singleTimestamp,proto3" json:"single_timestamp,omitempty"`
SingleStruct *structpb.Struct `protobuf:"bytes,103,opt,name=single_struct,json=singleStruct,proto3" json:"single_struct,omitempty"`
SingleValue *structpb.Value `protobuf:"bytes,104,opt,name=single_value,json=singleValue,proto3" json:"single_value,omitempty"`
SingleInt64Wrapper *wrapperspb.Int64Value `protobuf:"bytes,105,opt,name=single_int64_wrapper,json=singleInt64Wrapper,proto3" json:"single_int64_wrapper,omitempty"`
SingleInt32Wrapper *wrapperspb.Int32Value `protobuf:"bytes,106,opt,name=single_int32_wrapper,json=singleInt32Wrapper,proto3" json:"single_int32_wrapper,omitempty"`
SingleDoubleWrapper *wrapperspb.DoubleValue `protobuf:"bytes,107,opt,name=single_double_wrapper,json=singleDoubleWrapper,proto3" json:"single_double_wrapper,omitempty"`
SingleFloatWrapper *wrapperspb.FloatValue `protobuf:"bytes,108,opt,name=single_float_wrapper,json=singleFloatWrapper,proto3" json:"single_float_wrapper,omitempty"`
SingleUint64Wrapper *wrapperspb.UInt64Value `protobuf:"bytes,109,opt,name=single_uint64_wrapper,json=singleUint64Wrapper,proto3" json:"single_uint64_wrapper,omitempty"`
SingleUint32Wrapper *wrapperspb.UInt32Value `protobuf:"bytes,110,opt,name=single_uint32_wrapper,json=singleUint32Wrapper,proto3" json:"single_uint32_wrapper,omitempty"`
SingleStringWrapper *wrapperspb.StringValue `protobuf:"bytes,111,opt,name=single_string_wrapper,json=singleStringWrapper,proto3" json:"single_string_wrapper,omitempty"`
SingleBoolWrapper *wrapperspb.BoolValue `protobuf:"bytes,112,opt,name=single_bool_wrapper,json=singleBoolWrapper,proto3" json:"single_bool_wrapper,omitempty"`
SingleBytesWrapper *wrapperspb.BytesValue `protobuf:"bytes,113,opt,name=single_bytes_wrapper,json=singleBytesWrapper,proto3" json:"single_bytes_wrapper,omitempty"`
// Types that are assignable to NestedType:
//
// *TestAllTypes_SingleNestedMessage
// *TestAllTypes_SingleNestedEnum
NestedType isTestAllTypes_NestedType `protobuf_oneof:"nested_type"`
RepeatedInt32 []int32 `protobuf:"varint,31,rep,packed,name=repeated_int32,json=repeatedInt32,proto3" json:"repeated_int32,omitempty"`
RepeatedInt64 []int64 `protobuf:"varint,32,rep,packed,name=repeated_int64,json=repeatedInt64,proto3" json:"repeated_int64,omitempty"`
RepeatedUint32 []uint32 `protobuf:"varint,33,rep,packed,name=repeated_uint32,json=repeatedUint32,proto3" json:"repeated_uint32,omitempty"`
RepeatedUint64 []uint64 `protobuf:"varint,34,rep,packed,name=repeated_uint64,json=repeatedUint64,proto3" json:"repeated_uint64,omitempty"`
RepeatedSint32 []int32 `protobuf:"zigzag32,35,rep,packed,name=repeated_sint32,json=repeatedSint32,proto3" json:"repeated_sint32,omitempty"`
RepeatedSint64 []int64 `protobuf:"zigzag64,36,rep,packed,name=repeated_sint64,json=repeatedSint64,proto3" json:"repeated_sint64,omitempty"`
RepeatedFixed32 []uint32 `protobuf:"fixed32,37,rep,packed,name=repeated_fixed32,json=repeatedFixed32,proto3" json:"repeated_fixed32,omitempty"`
RepeatedFixed64 []uint64 `protobuf:"fixed64,38,rep,packed,name=repeated_fixed64,json=repeatedFixed64,proto3" json:"repeated_fixed64,omitempty"`
RepeatedSfixed32 []int32 `protobuf:"fixed32,39,rep,packed,name=repeated_sfixed32,json=repeatedSfixed32,proto3" json:"repeated_sfixed32,omitempty"`
RepeatedSfixed64 []int64 `protobuf:"fixed64,40,rep,packed,name=repeated_sfixed64,json=repeatedSfixed64,proto3" json:"repeated_sfixed64,omitempty"`
RepeatedFloat []float32 `protobuf:"fixed32,41,rep,packed,name=repeated_float,json=repeatedFloat,proto3" json:"repeated_float,omitempty"`
RepeatedDouble []float64 `protobuf:"fixed64,42,rep,packed,name=repeated_double,json=repeatedDouble,proto3" json:"repeated_double,omitempty"`
RepeatedBool []bool `protobuf:"varint,43,rep,packed,name=repeated_bool,json=repeatedBool,proto3" json:"repeated_bool,omitempty"`
RepeatedString []string `protobuf:"bytes,44,rep,name=repeated_string,json=repeatedString,proto3" json:"repeated_string,omitempty"`
RepeatedBytes [][]byte `protobuf:"bytes,45,rep,name=repeated_bytes,json=repeatedBytes,proto3" json:"repeated_bytes,omitempty"`
RepeatedNestedMessage []*TestAllTypes_NestedMessage `protobuf:"bytes,48,rep,name=repeated_nested_message,json=repeatedNestedMessage,proto3" json:"repeated_nested_message,omitempty"`
RepeatedNestedEnum []TestAllTypes_NestedEnum `protobuf:"varint,51,rep,packed,name=repeated_nested_enum,json=repeatedNestedEnum,proto3,enum=google.expr.proto3.test.TestAllTypes_NestedEnum" json:"repeated_nested_enum,omitempty"`
RepeatedStringPiece []string `protobuf:"bytes,54,rep,name=repeated_string_piece,json=repeatedStringPiece,proto3" json:"repeated_string_piece,omitempty"`
RepeatedCord []string `protobuf:"bytes,55,rep,name=repeated_cord,json=repeatedCord,proto3" json:"repeated_cord,omitempty"`
RepeatedLazyMessage []*TestAllTypes_NestedMessage `protobuf:"bytes,57,rep,name=repeated_lazy_message,json=repeatedLazyMessage,proto3" json:"repeated_lazy_message,omitempty"`
MapStringString map[string]string `protobuf:"bytes,58,rep,name=map_string_string,json=mapStringString,proto3" json:"map_string_string,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
MapInt64NestedType map[int64]*NestedTestAllTypes `protobuf:"bytes,59,rep,name=map_int64_nested_type,json=mapInt64NestedType,proto3" json:"map_int64_nested_type,omitempty" protobuf_key:"varint,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
ImportedEnums []ImportedGlobalEnum `protobuf:"varint,60,rep,packed,name=imported_enums,json=importedEnums,proto3,enum=google.expr.proto3.test.ImportedGlobalEnum" json:"imported_enums,omitempty"`
}
func (x *TestAllTypes) Reset() {
*x = TestAllTypes{}
if protoimpl.UnsafeEnabled {
mi := &file_test_proto3pb_test_all_types_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *TestAllTypes) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*TestAllTypes) ProtoMessage() {}
func (x *TestAllTypes) ProtoReflect() protoreflect.Message {
mi := &file_test_proto3pb_test_all_types_proto_msgTypes[0]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use TestAllTypes.ProtoReflect.Descriptor instead.
func (*TestAllTypes) Descriptor() ([]byte, []int) {
return file_test_proto3pb_test_all_types_proto_rawDescGZIP(), []int{0}
}
func (x *TestAllTypes) GetSingleInt32() int32 {
if x != nil {
return x.SingleInt32
}
return 0
}
func (x *TestAllTypes) GetSingleInt64() int64 {
if x != nil {
return x.SingleInt64
}
return 0
}
func (x *TestAllTypes) GetSingleUint32() uint32 {
if x != nil {
return x.SingleUint32
}
return 0
}
func (x *TestAllTypes) GetSingleUint64() uint64 {
if x != nil {
return x.SingleUint64
}
return 0
}
func (x *TestAllTypes) GetSingleSint32() int32 {
if x != nil {
return x.SingleSint32
}
return 0
}
func (x *TestAllTypes) GetSingleSint64() int64 {
if x != nil {
return x.SingleSint64
}
return 0
}
func (x *TestAllTypes) GetSingleFixed32() uint32 {
if x != nil {
return x.SingleFixed32
}
return 0
}
func (x *TestAllTypes) GetSingleFixed64() uint64 {
if x != nil {
return x.SingleFixed64
}
return 0
}
func (x *TestAllTypes) GetSingleSfixed32() int32 {
if x != nil {
return x.SingleSfixed32
}
return 0
}
func (x *TestAllTypes) GetSingleSfixed64() int64 {
if x != nil {
return x.SingleSfixed64
}
return 0
}
func (x *TestAllTypes) GetSingleFloat() float32 {
if x != nil {
return x.SingleFloat
}
return 0
}
func (x *TestAllTypes) GetSingleDouble() float64 {
if x != nil {
return x.SingleDouble
}
return 0
}
func (x *TestAllTypes) GetSingleBool() bool {
if x != nil {
return x.SingleBool
}
return false
}
func (x *TestAllTypes) GetSingleString() string {
if x != nil {
return x.SingleString
}
return ""
}
func (x *TestAllTypes) GetSingleBytes() []byte {
if x != nil {
return x.SingleBytes
}
return nil
}
func (x *TestAllTypes) GetStandaloneEnum() TestAllTypes_NestedEnum {
if x != nil {
return x.StandaloneEnum
}
return TestAllTypes_FOO
}
func (x *TestAllTypes) GetSingleAny() *anypb.Any {
if x != nil {
return x.SingleAny
}
return nil
}
func (x *TestAllTypes) GetSingleDuration() *durationpb.Duration {
if x != nil {
return x.SingleDuration
}
return nil
}
func (x *TestAllTypes) GetSingleTimestamp() *timestamppb.Timestamp {
if x != nil {
return x.SingleTimestamp
}
return nil
}
func (x *TestAllTypes) GetSingleStruct() *structpb.Struct {
if x != nil {
return x.SingleStruct
}
return nil
}
func (x *TestAllTypes) GetSingleValue() *structpb.Value {
if x != nil {
return x.SingleValue
}
return nil
}
func (x *TestAllTypes) GetSingleInt64Wrapper() *wrapperspb.Int64Value {
if x != nil {
return x.SingleInt64Wrapper
}
return nil
}
func (x *TestAllTypes) GetSingleInt32Wrapper() *wrapperspb.Int32Value {
if x != nil {
return x.SingleInt32Wrapper
}
return nil
}
func (x *TestAllTypes) GetSingleDoubleWrapper() *wrapperspb.DoubleValue {
if x != nil {
return x.SingleDoubleWrapper
}
return nil
}
func (x *TestAllTypes) GetSingleFloatWrapper() *wrapperspb.FloatValue {
if x != nil {
return x.SingleFloatWrapper
}
return nil
}
func (x *TestAllTypes) GetSingleUint64Wrapper() *wrapperspb.UInt64Value {
if x != nil {
return x.SingleUint64Wrapper
}
return nil
}
func (x *TestAllTypes) GetSingleUint32Wrapper() *wrapperspb.UInt32Value {
if x != nil {
return x.SingleUint32Wrapper
}
return nil
}
func (x *TestAllTypes) GetSingleStringWrapper() *wrapperspb.StringValue {
if x != nil {
return x.SingleStringWrapper
}
return nil
}
func (x *TestAllTypes) GetSingleBoolWrapper() *wrapperspb.BoolValue {
if x != nil {
return x.SingleBoolWrapper
}
return nil
}
func (x *TestAllTypes) GetSingleBytesWrapper() *wrapperspb.BytesValue {
if x != nil {
return x.SingleBytesWrapper
}
return nil
}
func (m *TestAllTypes) GetNestedType() isTestAllTypes_NestedType {
if m != nil {
return m.NestedType
}
return nil
}
func (x *TestAllTypes) GetSingleNestedMessage() *TestAllTypes_NestedMessage {
if x, ok := x.GetNestedType().(*TestAllTypes_SingleNestedMessage); ok {
return x.SingleNestedMessage
}
return nil
}
func (x *TestAllTypes) GetSingleNestedEnum() TestAllTypes_NestedEnum {
if x, ok := x.GetNestedType().(*TestAllTypes_SingleNestedEnum); ok {
return x.SingleNestedEnum
}
return TestAllTypes_FOO
}
func (x *TestAllTypes) GetRepeatedInt32() []int32 {
if x != nil {
return x.RepeatedInt32
}
return nil
}
func (x *TestAllTypes) GetRepeatedInt64() []int64 {
if x != nil {
return x.RepeatedInt64
}
return nil
}
func (x *TestAllTypes) GetRepeatedUint32() []uint32 {
if x != nil {
return x.RepeatedUint32
}
return nil
}
func (x *TestAllTypes) GetRepeatedUint64() []uint64 {
if x != nil {
return x.RepeatedUint64
}
return nil
}
func (x *TestAllTypes) GetRepeatedSint32() []int32 {
if x != nil {
return x.RepeatedSint32
}
return nil
}
func (x *TestAllTypes) GetRepeatedSint64() []int64 {
if x != nil {
return x.RepeatedSint64
}
return nil
}
func (x *TestAllTypes) GetRepeatedFixed32() []uint32 {
if x != nil {
return x.RepeatedFixed32
}
return nil
}
func (x *TestAllTypes) GetRepeatedFixed64() []uint64 {
if x != nil {
return x.RepeatedFixed64
}
return nil
}
func (x *TestAllTypes) GetRepeatedSfixed32() []int32 {
if x != nil {
return x.RepeatedSfixed32
}
return nil
}
func (x *TestAllTypes) GetRepeatedSfixed64() []int64 {
if x != nil {
return x.RepeatedSfixed64
}
return nil
}
func (x *TestAllTypes) GetRepeatedFloat() []float32 {
if x != nil {
return x.RepeatedFloat
}
return nil
}
func (x *TestAllTypes) GetRepeatedDouble() []float64 {
if x != nil {
return x.RepeatedDouble
}
return nil
}
func (x *TestAllTypes) GetRepeatedBool() []bool {
if x != nil {
return x.RepeatedBool
}
return nil
}
func (x *TestAllTypes) GetRepeatedString() []string {
if x != nil {
return x.RepeatedString
}
return nil
}
func (x *TestAllTypes) GetRepeatedBytes() [][]byte {
if x != nil {
return x.RepeatedBytes
}
return nil
}
func (x *TestAllTypes) GetRepeatedNestedMessage() []*TestAllTypes_NestedMessage {
if x != nil {
return x.RepeatedNestedMessage
}
return nil
}
func (x *TestAllTypes) GetRepeatedNestedEnum() []TestAllTypes_NestedEnum {
if x != nil {
return x.RepeatedNestedEnum
}
return nil
}
func (x *TestAllTypes) GetRepeatedStringPiece() []string {
if x != nil {
return x.RepeatedStringPiece
}
return nil
}
func (x *TestAllTypes) GetRepeatedCord() []string {
if x != nil {
return x.RepeatedCord
}
return nil
}
func (x *TestAllTypes) GetRepeatedLazyMessage() []*TestAllTypes_NestedMessage {
if x != nil {
return x.RepeatedLazyMessage
}
return nil
}
func (x *TestAllTypes) GetMapStringString() map[string]string {
if x != nil {
return x.MapStringString
}
return nil
}
func (x *TestAllTypes) GetMapInt64NestedType() map[int64]*NestedTestAllTypes {
if x != nil {
return x.MapInt64NestedType
}
return nil
}
func (x *TestAllTypes) GetImportedEnums() []ImportedGlobalEnum {
if x != nil {
return x.ImportedEnums
}
return nil
}
type isTestAllTypes_NestedType interface {
isTestAllTypes_NestedType()
}
type TestAllTypes_SingleNestedMessage struct {
SingleNestedMessage *TestAllTypes_NestedMessage `protobuf:"bytes,18,opt,name=single_nested_message,json=singleNestedMessage,proto3,oneof"`
}
type TestAllTypes_SingleNestedEnum struct {
SingleNestedEnum TestAllTypes_NestedEnum `protobuf:"varint,21,opt,name=single_nested_enum,json=singleNestedEnum,proto3,enum=google.expr.proto3.test.TestAllTypes_NestedEnum,oneof"`
}
func (*TestAllTypes_SingleNestedMessage) isTestAllTypes_NestedType() {}
func (*TestAllTypes_SingleNestedEnum) isTestAllTypes_NestedType() {}
type NestedTestAllTypes struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Child *NestedTestAllTypes `protobuf:"bytes,1,opt,name=child,proto3" json:"child,omitempty"`
Payload *TestAllTypes `protobuf:"bytes,2,opt,name=payload,proto3" json:"payload,omitempty"`
}
func (x *NestedTestAllTypes) Reset() {
*x = NestedTestAllTypes{}
if protoimpl.UnsafeEnabled {
mi := &file_test_proto3pb_test_all_types_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *NestedTestAllTypes) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*NestedTestAllTypes) ProtoMessage() {}
func (x *NestedTestAllTypes) ProtoReflect() protoreflect.Message {
mi := &file_test_proto3pb_test_all_types_proto_msgTypes[1]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use NestedTestAllTypes.ProtoReflect.Descriptor instead.
func (*NestedTestAllTypes) Descriptor() ([]byte, []int) {
return file_test_proto3pb_test_all_types_proto_rawDescGZIP(), []int{1}
}
func (x *NestedTestAllTypes) GetChild() *NestedTestAllTypes {
if x != nil {
return x.Child
}
return nil
}
func (x *NestedTestAllTypes) GetPayload() *TestAllTypes {
if x != nil {
return x.Payload
}
return nil
}
type TestAllTypes_NestedMessage struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Bb int32 `protobuf:"varint,1,opt,name=bb,proto3" json:"bb,omitempty"`
}
func (x *TestAllTypes_NestedMessage) Reset() {
*x = TestAllTypes_NestedMessage{}
if protoimpl.UnsafeEnabled {
mi := &file_test_proto3pb_test_all_types_proto_msgTypes[2]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *TestAllTypes_NestedMessage) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*TestAllTypes_NestedMessage) ProtoMessage() {}
func (x *TestAllTypes_NestedMessage) ProtoReflect() protoreflect.Message {
mi := &file_test_proto3pb_test_all_types_proto_msgTypes[2]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use TestAllTypes_NestedMessage.ProtoReflect.Descriptor instead.
func (*TestAllTypes_NestedMessage) Descriptor() ([]byte, []int) {
return file_test_proto3pb_test_all_types_proto_rawDescGZIP(), []int{0, 0}
}
func (x *TestAllTypes_NestedMessage) GetBb() int32 {
if x != nil {
return x.Bb
}
return 0
}
var File_test_proto3pb_test_all_types_proto protoreflect.FileDescriptor
var file_test_proto3pb_test_all_types_proto_rawDesc = []byte{
0x0a, 0x22, 0x74, 0x65, 0x73, 0x74, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, 0x70, 0x62, 0x2f,
0x74, 0x65, 0x73, 0x74, 0x5f, 0x61, 0x6c, 0x6c, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x70,
0x72, 0x6f, 0x74, 0x6f, 0x12, 0x17, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x65, 0x78, 0x70,
0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x67,
0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x61,
0x6e, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65,
0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69,
0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1c, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65,
0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74,
0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70,
0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d,
0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f,
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x72,
0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1f, 0x74, 0x65, 0x73, 0x74, 0x2f, 0x70, 0x72,
0x6f, 0x74, 0x6f, 0x33, 0x70, 0x62, 0x2f, 0x74, 0x65, 0x73, 0x74, 0x5f, 0x69, 0x6d, 0x70, 0x6f,
0x72, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xab, 0x1b, 0x0a, 0x0c, 0x54, 0x65, 0x73,
0x74, 0x41, 0x6c, 0x6c, 0x54, 0x79, 0x70, 0x65, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x69, 0x6e,
0x67, 0x6c, 0x65, 0x5f, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52,
0x0b, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x49, 0x6e, 0x74, 0x33, 0x32, 0x12, 0x21, 0x0a, 0x0c,
0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x5f, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x18, 0x02, 0x20, 0x01,
0x28, 0x03, 0x52, 0x0b, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x49, 0x6e, 0x74, 0x36, 0x34, 0x12,
0x23, 0x0a, 0x0d, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x5f, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32,
0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0c, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x55, 0x69,
0x6e, 0x74, 0x33, 0x32, 0x12, 0x23, 0x0a, 0x0d, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x5f, 0x75,
0x69, 0x6e, 0x74, 0x36, 0x34, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0c, 0x73, 0x69, 0x6e,
0x67, 0x6c, 0x65, 0x55, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x12, 0x23, 0x0a, 0x0d, 0x73, 0x69, 0x6e,
0x67, 0x6c, 0x65, 0x5f, 0x73, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x18, 0x05, 0x20, 0x01, 0x28, 0x11,
0x52, 0x0c, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x53, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x12, 0x23,
0x0a, 0x0d, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x5f, 0x73, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x18,
0x06, 0x20, 0x01, 0x28, 0x12, 0x52, 0x0c, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x53, 0x69, 0x6e,
0x74, 0x36, 0x34, 0x12, 0x25, 0x0a, 0x0e, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x5f, 0x66, 0x69,
0x78, 0x65, 0x64, 0x33, 0x32, 0x18, 0x07, 0x20, 0x01, 0x28, 0x07, 0x52, 0x0d, 0x73, 0x69, 0x6e,
0x67, 0x6c, 0x65, 0x46, 0x69, 0x78, 0x65, 0x64, 0x33, 0x32, 0x12, 0x25, 0x0a, 0x0e, 0x73, 0x69,
0x6e, 0x67, 0x6c, 0x65, 0x5f, 0x66, 0x69, 0x78, 0x65, 0x64, 0x36, 0x34, 0x18, 0x08, 0x20, 0x01,
0x28, 0x06, 0x52, 0x0d, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x46, 0x69, 0x78, 0x65, 0x64, 0x36,
0x34, 0x12, 0x27, 0x0a, 0x0f, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x5f, 0x73, 0x66, 0x69, 0x78,
0x65, 0x64, 0x33, 0x32, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0f, 0x52, 0x0e, 0x73, 0x69, 0x6e, 0x67,
0x6c, 0x65, 0x53, 0x66, 0x69, 0x78, 0x65, 0x64, 0x33, 0x32, 0x12, 0x27, 0x0a, 0x0f, 0x73, 0x69,
0x6e, 0x67, 0x6c, 0x65, 0x5f, 0x73, 0x66, 0x69, 0x78, 0x65, 0x64, 0x36, 0x34, 0x18, 0x0a, 0x20,
0x01, 0x28, 0x10, 0x52, 0x0e, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x53, 0x66, 0x69, 0x78, 0x65,
0x64, 0x36, 0x34, 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x5f, 0x66, 0x6c,
0x6f, 0x61, 0x74, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x02, 0x52, 0x0b, 0x73, 0x69, 0x6e, 0x67, 0x6c,
0x65, 0x46, 0x6c, 0x6f, 0x61, 0x74, 0x12, 0x23, 0x0a, 0x0d, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65,
0x5f, 0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0c, 0x73,
0x69, 0x6e, 0x67, 0x6c, 0x65, 0x44, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x73,
0x69, 0x6e, 0x67, 0x6c, 0x65, 0x5f, 0x62, 0x6f, 0x6f, 0x6c, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x08,
0x52, 0x0a, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x42, 0x6f, 0x6f, 0x6c, 0x12, 0x23, 0x0a, 0x0d,
0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x5f, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x18, 0x0e, 0x20,
0x01, 0x28, 0x09, 0x52, 0x0c, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x53, 0x74, 0x72, 0x69, 0x6e,
0x67, 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x5f, 0x62, 0x79, 0x74, 0x65,
0x73, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x42,
0x79, 0x74, 0x65, 0x73, 0x12, 0x59, 0x0a, 0x0f, 0x73, 0x74, 0x61, 0x6e, 0x64, 0x61, 0x6c, 0x6f,
0x6e, 0x65, 0x5f, 0x65, 0x6e, 0x75, 0x6d, 0x18, 0x16, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x30, 0x2e,
0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x65, 0x78, 0x70, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74,
0x6f, 0x33, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x54, 0x65, 0x73, 0x74, 0x41, 0x6c, 0x6c, 0x54,
0x79, 0x70, 0x65, 0x73, 0x2e, 0x4e, 0x65, 0x73, 0x74, 0x65, 0x64, 0x45, 0x6e, 0x75, 0x6d, 0x52,
0x0e, 0x73, 0x74, 0x61, 0x6e, 0x64, 0x61, 0x6c, 0x6f, 0x6e, 0x65, 0x45, 0x6e, 0x75, 0x6d, 0x12,
0x33, 0x0a, 0x0a, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x5f, 0x61, 0x6e, 0x79, 0x18, 0x64, 0x20,
0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f,
0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x41, 0x6e, 0x79, 0x52, 0x09, 0x73, 0x69, 0x6e, 0x67, 0x6c,
0x65, 0x41, 0x6e, 0x79, 0x12, 0x42, 0x0a, 0x0f, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x5f, 0x64,
0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x65, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e,
0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e,
0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0e, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65,
0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x45, 0x0a, 0x10, 0x73, 0x69, 0x6e, 0x67,
0x6c, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x66, 0x20, 0x01,
0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74,
0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0f,
0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12,
0x3c, 0x0a, 0x0d, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x5f, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74,
0x18, 0x67, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e,
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52,
0x0c, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x12, 0x39, 0x0a,
0x0c, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x68, 0x20,
0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f,
0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0b, 0x73, 0x69, 0x6e,
0x67, 0x6c, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x4d, 0x0a, 0x14, 0x73, 0x69, 0x6e, 0x67,
0x6c, 0x65, 0x5f, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x5f, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x72,
0x18, 0x69, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e,
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x49, 0x6e, 0x74, 0x36, 0x34, 0x56, 0x61,
0x6c, 0x75, 0x65, 0x52, 0x12, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x49, 0x6e, 0x74, 0x36, 0x34,
0x57, 0x72, 0x61, 0x70, 0x70, 0x65, 0x72, 0x12, 0x4d, 0x0a, 0x14, 0x73, 0x69, 0x6e, 0x67, 0x6c,
0x65, 0x5f, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x72, 0x18,
0x6a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70,
0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x49, 0x6e, 0x74, 0x33, 0x32, 0x56, 0x61, 0x6c,
0x75, 0x65, 0x52, 0x12, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x49, 0x6e, 0x74, 0x33, 0x32, 0x57,
0x72, 0x61, 0x70, 0x70, 0x65, 0x72, 0x12, 0x50, 0x0a, 0x15, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65,
0x5f, 0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x5f, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x72, 0x18,
0x6b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70,
0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x56, 0x61,
0x6c, 0x75, 0x65, 0x52, 0x13, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x44, 0x6f, 0x75, 0x62, 0x6c,
0x65, 0x57, 0x72, 0x61, 0x70, 0x70, 0x65, 0x72, 0x12, 0x4d, 0x0a, 0x14, 0x73, 0x69, 0x6e, 0x67,
0x6c, 0x65, 0x5f, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x5f, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x72,
0x18, 0x6c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e,
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x46, 0x6c, 0x6f, 0x61, 0x74, 0x56, 0x61,
0x6c, 0x75, 0x65, 0x52, 0x12, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x46, 0x6c, 0x6f, 0x61, 0x74,
0x57, 0x72, 0x61, 0x70, 0x70, 0x65, 0x72, 0x12, 0x50, 0x0a, 0x15, 0x73, 0x69, 0x6e, 0x67, 0x6c,
0x65, 0x5f, 0x75, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x5f, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x72,
0x18, 0x6d, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e,
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x55, 0x49, 0x6e, 0x74, 0x36, 0x34, 0x56,
0x61, 0x6c, 0x75, 0x65, 0x52, 0x13, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x55, 0x69, 0x6e, 0x74,
0x36, 0x34, 0x57, 0x72, 0x61, 0x70, 0x70, 0x65, 0x72, 0x12, 0x50, 0x0a, 0x15, 0x73, 0x69, 0x6e,
0x67, 0x6c, 0x65, 0x5f, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x77, 0x72, 0x61, 0x70, 0x70,
0x65, 0x72, 0x18, 0x6e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c,
0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x55, 0x49, 0x6e, 0x74, 0x33,
0x32, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x13, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x55, 0x69,
0x6e, 0x74, 0x33, 0x32, 0x57, 0x72, 0x61, 0x70, 0x70, 0x65, 0x72, 0x12, 0x50, 0x0a, 0x15, 0x73,
0x69, 0x6e, 0x67, 0x6c, 0x65, 0x5f, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x5f, 0x77, 0x72, 0x61,
0x70, 0x70, 0x65, 0x72, 0x18, 0x6f, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f,
0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72,
0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x13, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65,
0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x57, 0x72, 0x61, 0x70, 0x70, 0x65, 0x72, 0x12, 0x4a, 0x0a,
0x13, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x5f, 0x62, 0x6f, 0x6f, 0x6c, 0x5f, 0x77, 0x72, 0x61,
0x70, 0x70, 0x65, 0x72, 0x18, 0x70, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f,
0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x42, 0x6f, 0x6f,
0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x11, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x42, 0x6f,
0x6f, 0x6c, 0x57, 0x72, 0x61, 0x70, 0x70, 0x65, 0x72, 0x12, 0x4d, 0x0a, 0x14, 0x73, 0x69, 0x6e,
0x67, 0x6c, 0x65, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x5f, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65,
0x72, 0x18, 0x71, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65,
0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x42, 0x79, 0x74, 0x65, 0x73, 0x56,
0x61, 0x6c, 0x75, 0x65, 0x52, 0x12, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x42, 0x79, 0x74, 0x65,
0x73, 0x57, 0x72, 0x61, 0x70, 0x70, 0x65, 0x72, 0x12, 0x69, 0x0a, 0x15, 0x73, 0x69, 0x6e, 0x67,
0x6c, 0x65, 0x5f, 0x6e, 0x65, 0x73, 0x74, 0x65, 0x64, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67,
0x65, 0x18, 0x12, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x33, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65,
0x2e, 0x65, 0x78, 0x70, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, 0x2e, 0x74, 0x65, 0x73,
0x74, 0x2e, 0x54, 0x65, 0x73, 0x74, 0x41, 0x6c, 0x6c, 0x54, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x4e,
0x65, 0x73, 0x74, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x48, 0x00, 0x52, 0x13,
0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x4e, 0x65, 0x73, 0x74, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73,
0x61, 0x67, 0x65, 0x12, 0x60, 0x0a, 0x12, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x5f, 0x6e, 0x65,
0x73, 0x74, 0x65, 0x64, 0x5f, 0x65, 0x6e, 0x75, 0x6d, 0x18, 0x15, 0x20, 0x01, 0x28, 0x0e, 0x32,
0x30, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x65, 0x78, 0x70, 0x72, 0x2e, 0x70, 0x72,
0x6f, 0x74, 0x6f, 0x33, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x54, 0x65, 0x73, 0x74, 0x41, 0x6c,
0x6c, 0x54, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x4e, 0x65, 0x73, 0x74, 0x65, 0x64, 0x45, 0x6e, 0x75,
0x6d, 0x48, 0x00, 0x52, 0x10, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x4e, 0x65, 0x73, 0x74, 0x65,
0x64, 0x45, 0x6e, 0x75, 0x6d, 0x12, 0x25, 0x0a, 0x0e, 0x72, 0x65, 0x70, 0x65, 0x61, 0x74, 0x65,
0x64, 0x5f, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x18, 0x1f, 0x20, 0x03, 0x28, 0x05, 0x52, 0x0d, 0x72,
0x65, 0x70, 0x65, 0x61, 0x74, 0x65, 0x64, 0x49, 0x6e, 0x74, 0x33, 0x32, 0x12, 0x25, 0x0a, 0x0e,
0x72, 0x65, 0x70, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x18, 0x20,
0x20, 0x03, 0x28, 0x03, 0x52, 0x0d, 0x72, 0x65, 0x70, 0x65, 0x61, 0x74, 0x65, 0x64, 0x49, 0x6e,
0x74, 0x36, 0x34, 0x12, 0x27, 0x0a, 0x0f, 0x72, 0x65, 0x70, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f,
0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x18, 0x21, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x0e, 0x72, 0x65,
0x70, 0x65, 0x61, 0x74, 0x65, 0x64, 0x55, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x12, 0x27, 0x0a, 0x0f,
0x72, 0x65, 0x70, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x75, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x18,
0x22, 0x20, 0x03, 0x28, 0x04, 0x52, 0x0e, 0x72, 0x65, 0x70, 0x65, 0x61, 0x74, 0x65, 0x64, 0x55,
0x69, 0x6e, 0x74, 0x36, 0x34, 0x12, 0x27, 0x0a, 0x0f, 0x72, 0x65, 0x70, 0x65, 0x61, 0x74, 0x65,
0x64, 0x5f, 0x73, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x18, 0x23, 0x20, 0x03, 0x28, 0x11, 0x52, 0x0e,
0x72, 0x65, 0x70, 0x65, 0x61, 0x74, 0x65, 0x64, 0x53, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x12, 0x27,
0x0a, 0x0f, 0x72, 0x65, 0x70, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x73, 0x69, 0x6e, 0x74, 0x36,
0x34, 0x18, 0x24, 0x20, 0x03, 0x28, 0x12, 0x52, 0x0e, 0x72, 0x65, 0x70, 0x65, 0x61, 0x74, 0x65,
0x64, 0x53, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x12, 0x29, 0x0a, 0x10, 0x72, 0x65, 0x70, 0x65, 0x61,
0x74, 0x65, 0x64, 0x5f, 0x66, 0x69, 0x78, 0x65, 0x64, 0x33, 0x32, 0x18, 0x25, 0x20, 0x03, 0x28,
0x07, 0x52, 0x0f, 0x72, 0x65, 0x70, 0x65, 0x61, 0x74, 0x65, 0x64, 0x46, 0x69, 0x78, 0x65, 0x64,
0x33, 0x32, 0x12, 0x29, 0x0a, 0x10, 0x72, 0x65, 0x70, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x66,
0x69, 0x78, 0x65, 0x64, 0x36, 0x34, 0x18, 0x26, 0x20, 0x03, 0x28, 0x06, 0x52, 0x0f, 0x72, 0x65,
0x70, 0x65, 0x61, 0x74, 0x65, 0x64, 0x46, 0x69, 0x78, 0x65, 0x64, 0x36, 0x34, 0x12, 0x2b, 0x0a,
0x11, 0x72, 0x65, 0x70, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x73, 0x66, 0x69, 0x78, 0x65, 0x64,
0x33, 0x32, 0x18, 0x27, 0x20, 0x03, 0x28, 0x0f, 0x52, 0x10, 0x72, 0x65, 0x70, 0x65, 0x61, 0x74,
0x65, 0x64, 0x53, 0x66, 0x69, 0x78, 0x65, 0x64, 0x33, 0x32, 0x12, 0x2b, 0x0a, 0x11, 0x72, 0x65,
0x70, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x73, 0x66, 0x69, 0x78, 0x65, 0x64, 0x36, 0x34, 0x18,
0x28, 0x20, 0x03, 0x28, 0x10, 0x52, 0x10, 0x72, 0x65, 0x70, 0x65, 0x61, 0x74, 0x65, 0x64, 0x53,
0x66, 0x69, 0x78, 0x65, 0x64, 0x36, 0x34, 0x12, 0x25, 0x0a, 0x0e, 0x72, 0x65, 0x70, 0x65, 0x61,
0x74, 0x65, 0x64, 0x5f, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x18, 0x29, 0x20, 0x03, 0x28, 0x02, 0x52,
0x0d, 0x72, 0x65, 0x70, 0x65, 0x61, 0x74, 0x65, 0x64, 0x46, 0x6c, 0x6f, 0x61, 0x74, 0x12, 0x27,
0x0a, 0x0f, 0x72, 0x65, 0x70, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x64, 0x6f, 0x75, 0x62, 0x6c,
0x65, 0x18, 0x2a, 0x20, 0x03, 0x28, 0x01, 0x52, 0x0e, 0x72, 0x65, 0x70, 0x65, 0x61, 0x74, 0x65,
0x64, 0x44, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x72, 0x65, 0x70, 0x65, 0x61,
0x74, 0x65, 0x64, 0x5f, 0x62, 0x6f, 0x6f, 0x6c, 0x18, 0x2b, 0x20, 0x03, 0x28, 0x08, 0x52, 0x0c,
0x72, 0x65, 0x70, 0x65, 0x61, 0x74, 0x65, 0x64, 0x42, 0x6f, 0x6f, 0x6c, 0x12, 0x27, 0x0a, 0x0f,
0x72, 0x65, 0x70, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x18,
0x2c, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0e, 0x72, 0x65, 0x70, 0x65, 0x61, 0x74, 0x65, 0x64, 0x53,
0x74, 0x72, 0x69, 0x6e, 0x67, 0x12, 0x25, 0x0a, 0x0e, 0x72, 0x65, 0x70, 0x65, 0x61, 0x74, 0x65,
0x64, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x2d, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x0d, 0x72,
0x65, 0x70, 0x65, 0x61, 0x74, 0x65, 0x64, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x6b, 0x0a, 0x17,
0x72, 0x65, 0x70, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x6e, 0x65, 0x73, 0x74, 0x65, 0x64, 0x5f,
0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x30, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x33, 0x2e,
0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x65, 0x78, 0x70, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74,
0x6f, 0x33, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x54, 0x65, 0x73, 0x74, 0x41, 0x6c, 0x6c, 0x54,
0x79, 0x70, 0x65, 0x73, 0x2e, 0x4e, 0x65, 0x73, 0x74, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61,
0x67, 0x65, 0x52, 0x15, 0x72, 0x65, 0x70, 0x65, 0x61, 0x74, 0x65, 0x64, 0x4e, 0x65, 0x73, 0x74,
0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x62, 0x0a, 0x14, 0x72, 0x65, 0x70,
0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x6e, 0x65, 0x73, 0x74, 0x65, 0x64, 0x5f, 0x65, 0x6e, 0x75,
0x6d, 0x18, 0x33, 0x20, 0x03, 0x28, 0x0e, 0x32, 0x30, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65,
0x2e, 0x65, 0x78, 0x70, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, 0x2e, 0x74, 0x65, 0x73,
0x74, 0x2e, 0x54, 0x65, 0x73, 0x74, 0x41, 0x6c, 0x6c, 0x54, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x4e,
0x65, 0x73, 0x74, 0x65, 0x64, 0x45, 0x6e, 0x75, 0x6d, 0x52, 0x12, 0x72, 0x65, 0x70, 0x65, 0x61,
0x74, 0x65, 0x64, 0x4e, 0x65, 0x73, 0x74, 0x65, 0x64, 0x45, 0x6e, 0x75, 0x6d, 0x12, 0x36, 0x0a,
0x15, 0x72, 0x65, 0x70, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67,
0x5f, 0x70, 0x69, 0x65, 0x63, 0x65, 0x18, 0x36, 0x20, 0x03, 0x28, 0x09, 0x42, 0x02, 0x08, 0x02,
0x52, 0x13, 0x72, 0x65, 0x70, 0x65, 0x61, 0x74, 0x65, 0x64, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67,
0x50, 0x69, 0x65, 0x63, 0x65, 0x12, 0x27, 0x0a, 0x0d, 0x72, 0x65, 0x70, 0x65, 0x61, 0x74, 0x65,
0x64, 0x5f, 0x63, 0x6f, 0x72, 0x64, 0x18, 0x37, 0x20, 0x03, 0x28, 0x09, 0x42, 0x02, 0x08, 0x01,
0x52, 0x0c, 0x72, 0x65, 0x70, 0x65, 0x61, 0x74, 0x65, 0x64, 0x43, 0x6f, 0x72, 0x64, 0x12, 0x67,
0x0a, 0x15, 0x72, 0x65, 0x70, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x6c, 0x61, 0x7a, 0x79, 0x5f,
0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x39, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x33, 0x2e,
0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x65, 0x78, 0x70, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74,
0x6f, 0x33, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x54, 0x65, 0x73, 0x74, 0x41, 0x6c, 0x6c, 0x54,
0x79, 0x70, 0x65, 0x73, 0x2e, 0x4e, 0x65, 0x73, 0x74, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61,
0x67, 0x65, 0x52, 0x13, 0x72, 0x65, 0x70, 0x65, 0x61, 0x74, 0x65, 0x64, 0x4c, 0x61, 0x7a, 0x79,
0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x66, 0x0a, 0x11, 0x6d, 0x61, 0x70, 0x5f, 0x73,
0x74, 0x72, 0x69, 0x6e, 0x67, 0x5f, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x18, 0x3a, 0x20, 0x03,
0x28, 0x0b, 0x32, 0x3a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x65, 0x78, 0x70, 0x72,
0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x54, 0x65, 0x73,
0x74, 0x41, 0x6c, 0x6c, 0x54, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x4d, 0x61, 0x70, 0x53, 0x74, 0x72,
0x69, 0x6e, 0x67, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0f,
0x6d, 0x61, 0x70, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x12,
0x70, 0x0a, 0x15, 0x6d, 0x61, 0x70, 0x5f, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x5f, 0x6e, 0x65, 0x73,
0x74, 0x65, 0x64, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x3b, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x3d,
0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x65, 0x78, 0x70, 0x72, 0x2e, 0x70, 0x72, 0x6f,
0x74, 0x6f, 0x33, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x54, 0x65, 0x73, 0x74, 0x41, 0x6c, 0x6c,
0x54, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x4d, 0x61, 0x70, 0x49, 0x6e, 0x74, 0x36, 0x34, 0x4e, 0x65,
0x73, 0x74, 0x65, 0x64, 0x54, 0x79, 0x70, 0x65, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x12, 0x6d,
0x61, 0x70, 0x49, 0x6e, 0x74, 0x36, 0x34, 0x4e, 0x65, 0x73, 0x74, 0x65, 0x64, 0x54, 0x79, 0x70,
0x65, 0x12, 0x52, 0x0a, 0x0e, 0x69, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x5f, 0x65, 0x6e,
0x75, 0x6d, 0x73, 0x18, 0x3c, 0x20, 0x03, 0x28, 0x0e, 0x32, 0x2b, 0x2e, 0x67, 0x6f, 0x6f, 0x67,
0x6c, 0x65, 0x2e, 0x65, 0x78, 0x70, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, 0x2e, 0x74,
0x65, 0x73, 0x74, 0x2e, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x47, 0x6c, 0x6f, 0x62,
0x61, 0x6c, 0x45, 0x6e, 0x75, 0x6d, 0x52, 0x0d, 0x69, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64,
0x45, 0x6e, 0x75, 0x6d, 0x73, 0x1a, 0x1f, 0x0a, 0x0d, 0x4e, 0x65, 0x73, 0x74, 0x65, 0x64, 0x4d,
0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x62, 0x62, 0x18, 0x01, 0x20, 0x01,
0x28, 0x05, 0x52, 0x02, 0x62, 0x62, 0x1a, 0x42, 0x0a, 0x14, 0x4d, 0x61, 0x70, 0x53, 0x74, 0x72,
0x69, 0x6e, 0x67, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10,
0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79,
0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52,
0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x72, 0x0a, 0x17, 0x4d, 0x61,
0x70, 0x49, 0x6e, 0x74, 0x36, 0x34, 0x4e, 0x65, 0x73, 0x74, 0x65, 0x64, 0x54, 0x79, 0x70, 0x65,
0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01,
0x28, 0x03, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x41, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65,
0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e,
0x65, 0x78, 0x70, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, 0x2e, 0x74, 0x65, 0x73, 0x74,
0x2e, 0x4e, 0x65, 0x73, 0x74, 0x65, 0x64, 0x54, 0x65, 0x73, 0x74, 0x41, 0x6c, 0x6c, 0x54, 0x79,
0x70, 0x65, 0x73, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x27,
0x0a, 0x0a, 0x4e, 0x65, 0x73, 0x74, 0x65, 0x64, 0x45, 0x6e, 0x75, 0x6d, 0x12, 0x07, 0x0a, 0x03,
0x46, 0x4f, 0x4f, 0x10, 0x00, 0x12, 0x07, 0x0a, 0x03, 0x42, 0x41, 0x52, 0x10, 0x01, 0x12, 0x07,
0x0a, 0x03, 0x42, 0x41, 0x5a, 0x10, 0x02, 0x42, 0x0d, 0x0a, 0x0b, 0x6e, 0x65, 0x73, 0x74, 0x65,
0x64, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x22, 0x98, 0x01, 0x0a, 0x12, 0x4e, 0x65, 0x73, 0x74, 0x65,
0x64, 0x54, 0x65, 0x73, 0x74, 0x41, 0x6c, 0x6c, 0x54, 0x79, 0x70, 0x65, 0x73, 0x12, 0x41, 0x0a,
0x05, 0x63, 0x68, 0x69, 0x6c, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x67,
0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x65, 0x78, 0x70, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
0x33, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x4e, 0x65, 0x73, 0x74, 0x65, 0x64, 0x54, 0x65, 0x73,
0x74, 0x41, 0x6c, 0x6c, 0x54, 0x79, 0x70, 0x65, 0x73, 0x52, 0x05, 0x63, 0x68, 0x69, 0x6c, 0x64,
0x12, 0x3f, 0x0a, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28,
0x0b, 0x32, 0x25, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x65, 0x78, 0x70, 0x72, 0x2e,
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x54, 0x65, 0x73, 0x74,
0x41, 0x6c, 0x6c, 0x54, 0x79, 0x70, 0x65, 0x73, 0x52, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61,
0x64, 0x2a, 0x27, 0x0a, 0x0a, 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x45, 0x6e, 0x75, 0x6d, 0x12,
0x07, 0x0a, 0x03, 0x47, 0x4f, 0x4f, 0x10, 0x00, 0x12, 0x07, 0x0a, 0x03, 0x47, 0x41, 0x52, 0x10,
0x01, 0x12, 0x07, 0x0a, 0x03, 0x47, 0x41, 0x5a, 0x10, 0x02, 0x42, 0x28, 0x5a, 0x26, 0x67, 0x69,
0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f,
0x63, 0x65, 0x6c, 0x2d, 0x67, 0x6f, 0x2f, 0x74, 0x65, 0x73, 0x74, 0x2f, 0x70, 0x72, 0x6f, 0x74,
0x6f, 0x33, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
file_test_proto3pb_test_all_types_proto_rawDescOnce sync.Once
file_test_proto3pb_test_all_types_proto_rawDescData = file_test_proto3pb_test_all_types_proto_rawDesc
)
func file_test_proto3pb_test_all_types_proto_rawDescGZIP() []byte {
file_test_proto3pb_test_all_types_proto_rawDescOnce.Do(func() {
file_test_proto3pb_test_all_types_proto_rawDescData = protoimpl.X.CompressGZIP(file_test_proto3pb_test_all_types_proto_rawDescData)
})
return file_test_proto3pb_test_all_types_proto_rawDescData
}
var file_test_proto3pb_test_all_types_proto_enumTypes = make([]protoimpl.EnumInfo, 2)
var file_test_proto3pb_test_all_types_proto_msgTypes = make([]protoimpl.MessageInfo, 5)
var file_test_proto3pb_test_all_types_proto_goTypes = []interface{}{
(GlobalEnum)(0), // 0: google.expr.proto3.test.GlobalEnum
(TestAllTypes_NestedEnum)(0), // 1: google.expr.proto3.test.TestAllTypes.NestedEnum
(*TestAllTypes)(nil), // 2: google.expr.proto3.test.TestAllTypes
(*NestedTestAllTypes)(nil), // 3: google.expr.proto3.test.NestedTestAllTypes
(*TestAllTypes_NestedMessage)(nil), // 4: google.expr.proto3.test.TestAllTypes.NestedMessage
nil, // 5: google.expr.proto3.test.TestAllTypes.MapStringStringEntry
nil, // 6: google.expr.proto3.test.TestAllTypes.MapInt64NestedTypeEntry
(*anypb.Any)(nil), // 7: google.protobuf.Any
(*durationpb.Duration)(nil), // 8: google.protobuf.Duration
(*timestamppb.Timestamp)(nil), // 9: google.protobuf.Timestamp
(*structpb.Struct)(nil), // 10: google.protobuf.Struct
(*structpb.Value)(nil), // 11: google.protobuf.Value
(*wrapperspb.Int64Value)(nil), // 12: google.protobuf.Int64Value
(*wrapperspb.Int32Value)(nil), // 13: google.protobuf.Int32Value
(*wrapperspb.DoubleValue)(nil), // 14: google.protobuf.DoubleValue
(*wrapperspb.FloatValue)(nil), // 15: google.protobuf.FloatValue
(*wrapperspb.UInt64Value)(nil), // 16: google.protobuf.UInt64Value
(*wrapperspb.UInt32Value)(nil), // 17: google.protobuf.UInt32Value
(*wrapperspb.StringValue)(nil), // 18: google.protobuf.StringValue
(*wrapperspb.BoolValue)(nil), // 19: google.protobuf.BoolValue
(*wrapperspb.BytesValue)(nil), // 20: google.protobuf.BytesValue
(ImportedGlobalEnum)(0), // 21: google.expr.proto3.test.ImportedGlobalEnum
}
var file_test_proto3pb_test_all_types_proto_depIdxs = []int32{
1, // 0: google.expr.proto3.test.TestAllTypes.standalone_enum:type_name -> google.expr.proto3.test.TestAllTypes.NestedEnum
7, // 1: google.expr.proto3.test.TestAllTypes.single_any:type_name -> google.protobuf.Any
8, // 2: google.expr.proto3.test.TestAllTypes.single_duration:type_name -> google.protobuf.Duration
9, // 3: google.expr.proto3.test.TestAllTypes.single_timestamp:type_name -> google.protobuf.Timestamp
10, // 4: google.expr.proto3.test.TestAllTypes.single_struct:type_name -> google.protobuf.Struct
11, // 5: google.expr.proto3.test.TestAllTypes.single_value:type_name -> google.protobuf.Value
12, // 6: google.expr.proto3.test.TestAllTypes.single_int64_wrapper:type_name -> google.protobuf.Int64Value
13, // 7: google.expr.proto3.test.TestAllTypes.single_int32_wrapper:type_name -> google.protobuf.Int32Value
14, // 8: google.expr.proto3.test.TestAllTypes.single_double_wrapper:type_name -> google.protobuf.DoubleValue
15, // 9: google.expr.proto3.test.TestAllTypes.single_float_wrapper:type_name -> google.protobuf.FloatValue
16, // 10: google.expr.proto3.test.TestAllTypes.single_uint64_wrapper:type_name -> google.protobuf.UInt64Value
17, // 11: google.expr.proto3.test.TestAllTypes.single_uint32_wrapper:type_name -> google.protobuf.UInt32Value
18, // 12: google.expr.proto3.test.TestAllTypes.single_string_wrapper:type_name -> google.protobuf.StringValue
19, // 13: google.expr.proto3.test.TestAllTypes.single_bool_wrapper:type_name -> google.protobuf.BoolValue
20, // 14: google.expr.proto3.test.TestAllTypes.single_bytes_wrapper:type_name -> google.protobuf.BytesValue
4, // 15: google.expr.proto3.test.TestAllTypes.single_nested_message:type_name -> google.expr.proto3.test.TestAllTypes.NestedMessage
1, // 16: google.expr.proto3.test.TestAllTypes.single_nested_enum:type_name -> google.expr.proto3.test.TestAllTypes.NestedEnum
4, // 17: google.expr.proto3.test.TestAllTypes.repeated_nested_message:type_name -> google.expr.proto3.test.TestAllTypes.NestedMessage
1, // 18: google.expr.proto3.test.TestAllTypes.repeated_nested_enum:type_name -> google.expr.proto3.test.TestAllTypes.NestedEnum
4, // 19: google.expr.proto3.test.TestAllTypes.repeated_lazy_message:type_name -> google.expr.proto3.test.TestAllTypes.NestedMessage
5, // 20: google.expr.proto3.test.TestAllTypes.map_string_string:type_name -> google.expr.proto3.test.TestAllTypes.MapStringStringEntry
6, // 21: google.expr.proto3.test.TestAllTypes.map_int64_nested_type:type_name -> google.expr.proto3.test.TestAllTypes.MapInt64NestedTypeEntry
21, // 22: google.expr.proto3.test.TestAllTypes.imported_enums:type_name -> google.expr.proto3.test.ImportedGlobalEnum
3, // 23: google.expr.proto3.test.NestedTestAllTypes.child:type_name -> google.expr.proto3.test.NestedTestAllTypes
2, // 24: google.expr.proto3.test.NestedTestAllTypes.payload:type_name -> google.expr.proto3.test.TestAllTypes
3, // 25: google.expr.proto3.test.TestAllTypes.MapInt64NestedTypeEntry.value:type_name -> google.expr.proto3.test.NestedTestAllTypes
26, // [26:26] is the sub-list for method output_type
26, // [26:26] is the sub-list for method input_type
26, // [26:26] is the sub-list for extension type_name
26, // [26:26] is the sub-list for extension extendee
0, // [0:26] is the sub-list for field type_name
}
func init() { file_test_proto3pb_test_all_types_proto_init() }
func file_test_proto3pb_test_all_types_proto_init() {
if File_test_proto3pb_test_all_types_proto != nil {
return
}
file_test_proto3pb_test_import_proto_init()
if !protoimpl.UnsafeEnabled {
file_test_proto3pb_test_all_types_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*TestAllTypes); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_test_proto3pb_test_all_types_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*NestedTestAllTypes); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_test_proto3pb_test_all_types_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*TestAllTypes_NestedMessage); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
}
file_test_proto3pb_test_all_types_proto_msgTypes[0].OneofWrappers = []interface{}{
(*TestAllTypes_SingleNestedMessage)(nil),
(*TestAllTypes_SingleNestedEnum)(nil),
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_test_proto3pb_test_all_types_proto_rawDesc,
NumEnums: 2,
NumMessages: 5,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_test_proto3pb_test_all_types_proto_goTypes,
DependencyIndexes: file_test_proto3pb_test_all_types_proto_depIdxs,
EnumInfos: file_test_proto3pb_test_all_types_proto_enumTypes,
MessageInfos: file_test_proto3pb_test_all_types_proto_msgTypes,
}.Build()
File_test_proto3pb_test_all_types_proto = out.File
file_test_proto3pb_test_all_types_proto_rawDesc = nil
file_test_proto3pb_test_all_types_proto_goTypes = nil
file_test_proto3pb_test_all_types_proto_depIdxs = nil
}
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.28.1
// protoc v3.21.5
// source: test/proto3pb/test_import.proto
package proto3pb
import (
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
type ImportedGlobalEnum int32
const (
ImportedGlobalEnum_IMPORT_FOO ImportedGlobalEnum = 0
ImportedGlobalEnum_IMPORT_BAR ImportedGlobalEnum = 1
ImportedGlobalEnum_IMPORT_BAZ ImportedGlobalEnum = 2
)
// Enum value maps for ImportedGlobalEnum.
var (
ImportedGlobalEnum_name = map[int32]string{
0: "IMPORT_FOO",
1: "IMPORT_BAR",
2: "IMPORT_BAZ",
}
ImportedGlobalEnum_value = map[string]int32{
"IMPORT_FOO": 0,
"IMPORT_BAR": 1,
"IMPORT_BAZ": 2,
}
)
func (x ImportedGlobalEnum) Enum() *ImportedGlobalEnum {
p := new(ImportedGlobalEnum)
*p = x
return p
}
func (x ImportedGlobalEnum) String() string {
return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
}
func (ImportedGlobalEnum) Descriptor() protoreflect.EnumDescriptor {
return file_test_proto3pb_test_import_proto_enumTypes[0].Descriptor()
}
func (ImportedGlobalEnum) Type() protoreflect.EnumType {
return &file_test_proto3pb_test_import_proto_enumTypes[0]
}
func (x ImportedGlobalEnum) Number() protoreflect.EnumNumber {
return protoreflect.EnumNumber(x)
}
// Deprecated: Use ImportedGlobalEnum.Descriptor instead.
func (ImportedGlobalEnum) EnumDescriptor() ([]byte, []int) {
return file_test_proto3pb_test_import_proto_rawDescGZIP(), []int{0}
}
var File_test_proto3pb_test_import_proto protoreflect.FileDescriptor
var file_test_proto3pb_test_import_proto_rawDesc = []byte{
0x0a, 0x1f, 0x74, 0x65, 0x73, 0x74, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, 0x70, 0x62, 0x2f,
0x74, 0x65, 0x73, 0x74, 0x5f, 0x69, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74,
0x6f, 0x12, 0x17, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x65, 0x78, 0x70, 0x72, 0x2e, 0x70,
0x72, 0x6f, 0x74, 0x6f, 0x33, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2a, 0x44, 0x0a, 0x12, 0x49, 0x6d,
0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x45, 0x6e, 0x75, 0x6d,
0x12, 0x0e, 0x0a, 0x0a, 0x49, 0x4d, 0x50, 0x4f, 0x52, 0x54, 0x5f, 0x46, 0x4f, 0x4f, 0x10, 0x00,
0x12, 0x0e, 0x0a, 0x0a, 0x49, 0x4d, 0x50, 0x4f, 0x52, 0x54, 0x5f, 0x42, 0x41, 0x52, 0x10, 0x01,
0x12, 0x0e, 0x0a, 0x0a, 0x49, 0x4d, 0x50, 0x4f, 0x52, 0x54, 0x5f, 0x42, 0x41, 0x5a, 0x10, 0x02,
0x42, 0x28, 0x5a, 0x26, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67,
0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x63, 0x65, 0x6c, 0x2d, 0x67, 0x6f, 0x2f, 0x74, 0x65, 0x73,
0x74, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74,
0x6f, 0x33,
}
var (
file_test_proto3pb_test_import_proto_rawDescOnce sync.Once
file_test_proto3pb_test_import_proto_rawDescData = file_test_proto3pb_test_import_proto_rawDesc
)
func file_test_proto3pb_test_import_proto_rawDescGZIP() []byte {
file_test_proto3pb_test_import_proto_rawDescOnce.Do(func() {
file_test_proto3pb_test_import_proto_rawDescData = protoimpl.X.CompressGZIP(file_test_proto3pb_test_import_proto_rawDescData)
})
return file_test_proto3pb_test_import_proto_rawDescData
}
var file_test_proto3pb_test_import_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
var file_test_proto3pb_test_import_proto_goTypes = []interface{}{
(ImportedGlobalEnum)(0), // 0: google.expr.proto3.test.ImportedGlobalEnum
}
var file_test_proto3pb_test_import_proto_depIdxs = []int32{
0, // [0:0] is the sub-list for method output_type
0, // [0:0] is the sub-list for method input_type
0, // [0:0] is the sub-list for extension type_name
0, // [0:0] is the sub-list for extension extendee
0, // [0:0] is the sub-list for field type_name
}
func init() { file_test_proto3pb_test_import_proto_init() }
func file_test_proto3pb_test_import_proto_init() {
if File_test_proto3pb_test_import_proto != nil {
return
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_test_proto3pb_test_import_proto_rawDesc,
NumEnums: 1,
NumMessages: 0,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_test_proto3pb_test_import_proto_goTypes,
DependencyIndexes: file_test_proto3pb_test_import_proto_depIdxs,
EnumInfos: file_test_proto3pb_test_import_proto_enumTypes,
}.Build()
File_test_proto3pb_test_import_proto = out.File
file_test_proto3pb_test_import_proto_rawDesc = nil
file_test_proto3pb_test_import_proto_goTypes = nil
file_test_proto3pb_test_import_proto_depIdxs = nil
}