cel

package
v0.7.3 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 25, 2021 License: Apache-2.0 Imports: 20 Imported by: 362

Documentation

Overview

Package cel defines the top-level interface for the Common Expression Language (CEL).

CEL is a non-Turing complete expression language designed to parse, check, and evaluate expressions against user-defined environments.

Example
// Create the CEL environment with declarations for the input attributes and
// the desired extension functions. In many cases the desired functionality will
// be present in a built-in function.
decls := Declarations(
	// Identifiers used within this expression.
	decls.NewVar("i", decls.String),
	decls.NewVar("you", decls.String),
	// Function to generate a greeting from one person to another.
	//    i.greet(you)
	decls.NewFunction("greet",
		decls.NewInstanceOverload("string_greet_string",
			[]*exprpb.Type{decls.String, decls.String},
			decls.String)))
e, err := NewEnv(decls)
if err != nil {
	log.Fatalf("environment creation error: %s\n", err)
}

// Compile the expression.
ast, iss := e.Compile("i.greet(you)")
if iss.Err() != nil {
	log.Fatalln(iss.Err())
}

// Create the program.
funcs := Functions(
	&functions.Overload{
		Operator: "string_greet_string",
		Binary: func(lhs ref.Val, rhs ref.Val) ref.Val {
			return types.String(
				fmt.Sprintf("Hello %s! Nice to meet you, I'm %s.\n", rhs, lhs))
		}})
prg, err := e.Program(ast, funcs)
if err != nil {
	log.Fatalf("program creation error: %s\n", err)
}

// Evaluate the program against some inputs. Note: the details return is not used.
out, _, err := prg.Eval(map[string]interface{}{
	// Native values are converted to CEL values under the covers.
	"i": "CEL",
	// Values may also be lazily supplied.
	"you": func() ref.Val { return types.String("world") },
})
if err != nil {
	log.Fatalf("runtime error: %s\n", err)
}

fmt.Println(out)
Output:

Hello world! Nice to meet you, I'm CEL.
Example (GlobalOverload)

ExampleGlobalOverload demonstrates how to define global overload function.

// Create the CEL environment with declarations for the input attributes and
// the desired extension functions. In many cases the desired functionality will
// be present in a built-in function.
decls := Declarations(
	// Identifiers used within this expression.
	decls.NewVar("i", decls.String),
	decls.NewVar("you", decls.String),
	// Function to generate shake_hands between two people.
	//    shake_hands(i,you)
	decls.NewFunction("shake_hands",
		decls.NewOverload("shake_hands_string_string",
			[]*exprpb.Type{decls.String, decls.String},
			decls.String)))
e, err := NewEnv(decls)
if err != nil {
	log.Fatalf("environment creation error: %s\n", err)
}

// Compile the expression.
ast, iss := e.Compile(`shake_hands(i,you)`)
if iss.Err() != nil {
	log.Fatalln(iss.Err())
}

// Create the program.
funcs := Functions(
	&functions.Overload{
		Operator: "shake_hands_string_string",
		Binary: func(lhs ref.Val, rhs ref.Val) ref.Val {
			s1, ok := lhs.(types.String)
			if !ok {
				return types.ValOrErr(lhs, "unexpected type '%v' passed to shake_hands", lhs.Type())
			}
			s2, ok := rhs.(types.String)
			if !ok {
				return types.ValOrErr(rhs, "unexpected type '%v' passed to shake_hands", rhs.Type())
			}
			return types.String(
				fmt.Sprintf("%s and %s are shaking hands.\n", s1, s2))
		}})
prg, err := e.Program(ast, funcs)
if err != nil {
	log.Fatalf("program creation error: %s\n", err)
}

// Evaluate the program against some inputs. Note: the details return is not used.
out, _, err := prg.Eval(map[string]interface{}{
	"i":   "CEL",
	"you": func() ref.Val { return types.String("world") },
})
if err != nil {
	log.Fatalf("runtime error: %s\n", err)
}

fmt.Println(out)
Output:

CEL and world are shaking hands.

Index

Examples

Constants

View Source
const (

	// Disallow heterogeneous aggregate (list, map) literals.
	// 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.
	// Affects checking.  Provides a subset of standard behavior.
	FeatureDisableDynamicAggregateLiterals
)

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.

Variables

This section is empty.

Functions

func AstToCheckedExpr

func AstToCheckedExpr(a *Ast) (*exprpb.CheckedExpr, error)

AstToCheckedExpr converts an Ast to an protobuf CheckedExpr value.

If the Ast.IsChecked() returns false, this conversion method will return an error.

func AstToParsedExpr

func AstToParsedExpr(a *Ast) (*exprpb.ParsedExpr, error)

AstToParsedExpr converts an Ast to an protobuf ParsedExpr value.

func AstToString added in v0.3.0

func AstToString(a *Ast) (string, error)

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 AttributePattern added in v0.4.0

func AttributePattern(varName string) *interpreter.AttributePattern

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.

See the interpreter.AttributePattern and interpreter.AttributeQualifierPattern for more info about how to create and manipulate AttributePattern values.

func EstimateCost added in v0.6.0

func EstimateCost(p Program) (min, max int64)

EstimateCost returns the heuristic cost interval for the program.

func FormatType added in v0.7.1

func FormatType(t *exprpb.Type) string

FormatType converts a type message into a string representation.

func NoVars

func NoVars() interpreter.Activation

NoVars returns an empty Activation.

func PartialVars added in v0.4.0

func PartialVars(vars interface{},
	unknowns ...*interpreter.AttributePattern) (interpreter.PartialActivation, error)

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.

The `vars` value may either be an interpreter.Activation or any valid input to the interpreter.NewActivation call.

Types

type Ast

type Ast struct {
	// contains filtered or unexported fields
}

Ast representing the checked or unchecked expression, its source, and related metadata such as source position information.

func CheckedExprToAst

func CheckedExprToAst(checkedExpr *exprpb.CheckedExpr) *Ast

CheckedExprToAst converts a checked expression proto message to an Ast.

func ParsedExprToAst

func ParsedExprToAst(parsedExpr *exprpb.ParsedExpr) *Ast

ParsedExprToAst converts a parsed expression proto message to an Ast.

func (*Ast) Expr

func (ast *Ast) Expr() *exprpb.Expr

Expr returns the proto serializable instance of the parsed/checked expression.

func (*Ast) IsChecked

func (ast *Ast) IsChecked() bool

IsChecked returns whether the Ast value has been successfully type-checked.

func (*Ast) ResultType

func (ast *Ast) ResultType() *exprpb.Type

ResultType returns the output type of the expression if the Ast has been type-checked, else returns decls.Dyn as the parse step cannot infer the type.

func (*Ast) Source

func (ast *Ast) Source() Source

Source returns a view of the input used to create the Ast. This source may be complete or constructed from the SourceInfo.

func (*Ast) SourceInfo

func (ast *Ast) SourceInfo() *exprpb.SourceInfo

SourceInfo returns character offset and newling position information about expression elements.

type Env

type Env struct {
	Container *containers.Container
	// contains filtered or unexported fields
}

Env encapsulates the context necessary to perform parsing, type checking, or generation of evaluable programs for different expressions.

func NewCustomEnv added in v0.4.0

func NewCustomEnv(opts ...EnvOption) (*Env, error)

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 NewEnv

func NewEnv(opts ...EnvOption) (*Env, error)

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 (*Env) Check

func (e *Env) Check(ast *Ast) (*Ast, *Issues)

Check performs type-checking on the input Ast and yields a checked Ast and/or set of Issues.

Checking 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 (*Env) Compile added in v0.4.0

func (e *Env) Compile(txt string) (*Ast, *Issues)

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 (*Env) CompileSource added in v0.4.0

func (e *Env) CompileSource(src common.Source) (*Ast, *Issues)

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 (*Env) Extend added in v0.3.2

func (e *Env) Extend(opts ...EnvOption) (*Env, error)

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 (*Env) HasFeature added in v0.5.1

func (e *Env) HasFeature(flag int) bool

HasFeature checks whether the environment enables the given feature flag, as enumerated in options.go.

func (*Env) Parse

func (e *Env) Parse(txt string) (*Ast, *Issues)

Parse parses the input expression value `txt` to a Ast and/or a set of Issues.

This form of Parse creates a common.Source value for the input `txt` and forwards to the ParseSource method.

func (*Env) ParseSource added in v0.4.0

func (e *Env) ParseSource(src common.Source) (*Ast, *Issues)

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 (*Env) Program

func (e *Env) Program(ast *Ast, opts ...ProgramOption) (Program, error)

Program generates an evaluable instance of the Ast within the environment (Env).

func (*Env) ResidualAst added in v0.4.0

func (e *Env) ResidualAst(a *Ast, details *EvalDetails) (*Ast, error)

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 (*Env) SetFeature added in v0.5.1

func (e *Env) SetFeature(flag int)

SetFeature sets the given feature flag, as enumerated in options.go.

func (*Env) TypeAdapter added in v0.2.0

func (e *Env) TypeAdapter() ref.TypeAdapter

TypeAdapter returns the `ref.TypeAdapter` configured for the environment.

func (*Env) TypeProvider added in v0.2.0

func (e *Env) TypeProvider() ref.TypeProvider

TypeProvider returns the `ref.TypeProvider` configured for the environment.

func (*Env) UnknownVars added in v0.4.0

func (e *Env) UnknownVars() interpreter.PartialActivation

UnknownVars returns an interpreter.PartialActivation which marks all variables declared in the Env as unknown AttributePattern values.

Note, the UnknownVars will behave the same as an interpreter.EmptyActivation unless the PartialAttributes option is provided as a ProgramOption.

type EnvOption

type EnvOption func(e *Env) (*Env, error)

EnvOption is a functional interface for configuring the environment.

func Abbrevs added in v0.6.0

func Abbrevs(qualifiedNames ...string) EnvOption

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 ClearMacros

func ClearMacros() EnvOption

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 Container

func Container(name string) EnvOption

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 CustomTypeAdapter added in v0.2.0

func CustomTypeAdapter(adapter ref.TypeAdapter) EnvOption

CustomTypeAdapter swaps the default ref.TypeAdapter implementation with a custom one.

Note: This option must be specified before the Types and TypeDescs options when used together.

func CustomTypeProvider

func CustomTypeProvider(provider ref.TypeProvider) EnvOption

CustomTypeProvider swaps the default ref.TypeProvider implementation with a custom one.

Note: This option must be specified before the Types and TypeDescs options when used together.

func Declarations

func Declarations(decls ...*exprpb.Decl) EnvOption

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.

func Features added in v0.5.1

func Features(flags ...int) EnvOption

Features sets the given feature flags. See list of Feature constants above.

func HomogeneousAggregateLiterals added in v0.2.0

func HomogeneousAggregateLiterals() EnvOption

HomogeneousAggregateLiterals option ensures that list and map literal entry types must agree during type-checking.

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 Lib added in v0.4.0

func Lib(l Library) EnvOption

Lib creates an EnvOption out of a Library, allowing libraries to be provided as functional args, and to be linked to each other.

func Macros

func Macros(macros ...parser.Macro) EnvOption

Macros option extends the macro set configured in the environment.

Note: This option must be specified after ClearMacros if used together.

func StdLib added in v0.4.0

func StdLib() EnvOption

StdLib returns an EnvOption for the standard library of CEL functions and macros.

func TypeDescs added in v0.2.0

func TypeDescs(descs ...interface{}) EnvOption

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 Types

func Types(addTypes ...interface{}) EnvOption

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.

type EvalDetails

type EvalDetails struct {
	// contains filtered or unexported fields
}

EvalDetails holds additional information observed during the Eval() call.

func (*EvalDetails) State

func (ed *EvalDetails) State() interpreter.EvalState

State of the evaluation, non-nil if the OptTrackState or OptExhaustiveEval is specified within EvalOptions.

type EvalOption

type EvalOption int

EvalOption indicates an evaluation option that may affect the evaluation behavior or information in the output result.

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. 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 is an PartialVars.
	OptPartialEval EvalOption = 1 << iota
)

type Issues

type Issues struct {
	// contains filtered or unexported fields
}

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.

func NewIssues added in v0.4.0

func NewIssues(errs *common.Errors) *Issues

NewIssues returns an Issues struct from a common.Errors object.

func (*Issues) Append added in v0.4.0

func (i *Issues) Append(other *Issues) *Issues

Append collects the issues from another Issues struct into a new Issues object.

func (*Issues) Err

func (i *Issues) Err() error

Err returns an error value if the issues list contains one or more errors.

func (*Issues) Errors

func (i *Issues) Errors() []common.Error

Errors returns the collection of errors encountered in more granular detail.

func (*Issues) String added in v0.4.0

func (i *Issues) String() string

String converts the issues to a suitable display string.

type Library added in v0.4.0

type Library interface {
	// CompileOptions returns a collection of funcitional 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
}

Library provides a collection of EnvOption and ProgramOption values used to confiugre 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 Program

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 `interpreter.Activation` or a `map[string]interface{}`.
	//
	// If the `OptTrackState` 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(vars interface{}) (ref.Val, *EvalDetails, error)
}

Program is an evaluable view of an Ast.

type ProgramOption

type ProgramOption func(p *prog) (*prog, error)

ProgramOption is a functional interface for configuring evaluation bindings and behaviors.

func CustomDecorator added in v0.6.0

CustomDecorator appends an InterpreterDecorator to the program.

InterpretableDecorators can be used to inspect, alter, or replace the Program plan.

func EvalOptions

func EvalOptions(opts ...EvalOption) ProgramOption

EvalOptions sets one or more evaluation options which may affect the evaluation or Result.

func Functions

func Functions(funcs ...*functions.Overload) ProgramOption

Functions adds function overloads that extend or override the set of CEL built-ins.

func Globals

func Globals(vars interface{}) ProgramOption

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.

The vars value may either be an `interpreter.Activation` instance or a `map[string]interface{}`.

type Source

type Source interface {
	common.Source
}

Source interface representing a user-provided expression.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL