1
0
mirror of https://github.com/kubernetes-sigs/descheduler.git synced 2026-01-27 05:46:13 +01:00
Files
descheduler/vendor/github.com/google/cel-go/interpreter/interpreter.go
Amir Alavi 1db6b615d1 [v0.34.0] bump to kubernetes 1.34 deps
Signed-off-by: Amir Alavi <amiralavi7@gmail.com>
2025-10-21 09:14:13 -04:00

274 lines
10 KiB
Go
Vendored

// 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())
}