rebase: update kubernetes to latest

updating the kubernetes release to the
latest in main go.mod

Signed-off-by: Madhu Rajanna <madhupr007@gmail.com>
This commit is contained in:
Madhu Rajanna
2024-08-19 10:01:33 +02:00
committed by mergify[bot]
parent 63c4c05b35
commit 5a66991bb3
2173 changed files with 98906 additions and 61334 deletions

View File

@ -1,12 +1,7 @@
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
package(
default_visibility = [
"//cel:__subpackages__",
"//checker:__subpackages__",
"//common:__subpackages__",
"//interpreter:__subpackages__",
],
default_visibility = ["//visibility:public"],
licenses = ["notice"], # Apache 2.0
)
@ -14,10 +9,14 @@ go_library(
name = "go_default_library",
srcs = [
"ast.go",
"conversion.go",
"expr.go",
"factory.go",
"navigable.go",
],
importpath = "github.com/google/cel-go/common/ast",
deps = [
"//common:go_default_library",
"//common/types:go_default_library",
"//common/types/ref:go_default_library",
"@org_golang_google_genproto_googleapis_api//expr/v1alpha1:go_default_library",
@ -29,7 +28,9 @@ go_test(
name = "go_default_test",
srcs = [
"ast_test.go",
"conversion_test.go",
"expr_test.go",
"navigable_test.go",
],
embed = [
":go_default_library",
@ -48,5 +49,6 @@ go_test(
"//test/proto3pb:go_default_library",
"@org_golang_google_genproto_googleapis_api//expr/v1alpha1:go_default_library",
"@org_golang_google_protobuf//proto:go_default_library",
"@org_golang_google_protobuf//encoding/prototext:go_default_library",
],
)
)

View File

@ -16,74 +16,361 @@
package ast
import (
"fmt"
"github.com/google/cel-go/common"
"github.com/google/cel-go/common/types"
"github.com/google/cel-go/common/types/ref"
structpb "google.golang.org/protobuf/types/known/structpb"
exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1"
)
// CheckedAST contains a protobuf expression and source info along with CEL-native type and reference information.
type CheckedAST struct {
Expr *exprpb.Expr
SourceInfo *exprpb.SourceInfo
TypeMap map[int64]*types.Type
ReferenceMap map[int64]*ReferenceInfo
// 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
}
// CheckedASTToCheckedExpr converts a CheckedAST to a CheckedExpr protobouf.
func CheckedASTToCheckedExpr(ast *CheckedAST) (*exprpb.CheckedExpr, error) {
refMap := make(map[int64]*exprpb.Reference, len(ast.ReferenceMap))
for id, ref := range ast.ReferenceMap {
r, err := ReferenceInfoToReferenceExpr(ref)
if err != nil {
return nil, err
}
refMap[id] = r
// Expr returns the root ast.Expr value in the AST.
func (a *AST) Expr() Expr {
if a == nil {
return nilExpr
}
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
}
return &exprpb.CheckedExpr{
Expr: ast.Expr,
SourceInfo: ast.SourceInfo,
ReferenceMap: refMap,
TypeMap: typeMap,
}, nil
return a.expr
}
// CheckedExprToCheckedAST converts a CheckedExpr protobuf to a CheckedAST instance.
func CheckedExprToCheckedAST(checked *exprpb.CheckedExpr) (*CheckedAST, error) {
refMap := make(map[int64]*ReferenceInfo, len(checked.GetReferenceMap()))
for id, ref := range checked.GetReferenceMap() {
r, err := ReferenceExprToReferenceInfo(ref)
if err != nil {
return nil, err
}
refMap[id] = r
// SourceInfo returns the source metadata associated with the parse / type-check passes.
func (a *AST) SourceInfo() *SourceInfo {
if a == nil {
return nil
}
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
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 &CheckedAST{
Expr: checked.GetExpr(),
SourceInfo: checked.GetSourceInfo(),
ReferenceMap: refMap,
TypeMap: typeMap,
}, nil
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
}
// 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
}
// 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 {
line := 1
col := int(o.Start)
for _, lineOffset := range s.LineOffsets() {
if lineOffset < o.Start {
line++
col = int(o.Start - lineOffset)
} else {
break
}
}
return common.NewLocation(line, col)
}
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 {
line := 1
col := int(o.Stop)
for _, lineOffset := range s.LineOffsets() {
if lineOffset < o.Stop {
line++
col = int(o.Stop - lineOffset)
} else {
break
}
}
return common.NewLocation(line, col)
}
return common.NoLocation
}
// 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
@ -149,78 +436,21 @@ func (r *ReferenceInfo) Equals(other *ReferenceInfo) bool {
return true
}
// ReferenceInfoToReferenceExpr converts a ReferenceInfo instance to a protobuf Reference suitable for serialization.
func ReferenceInfoToReferenceExpr(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
type maxIDVisitor struct {
maxID int64
*baseVisitor
}
// ReferenceExprToReferenceInfo converts a protobuf Reference into a CEL-native ReferenceInfo instance.
func ReferenceExprToReferenceInfo(ref *exprpb.Reference) (*ReferenceInfo, error) {
v, err := ConstantToVal(ref.GetValue())
if err != nil {
return nil, err
// 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()
}
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
// 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()
}
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) {
if c == nil {
return nil, nil
}
switch c.GetConstantKind().(type) {
case *exprpb.Constant_BoolValue:
return types.Bool(c.GetBoolValue()), nil
case *exprpb.Constant_BytesValue:
return types.Bytes(c.GetBytesValue()), nil
case *exprpb.Constant_DoubleValue:
return types.Double(c.GetDoubleValue()), nil
case *exprpb.Constant_Int64Value:
return types.Int(c.GetInt64Value()), nil
case *exprpb.Constant_NullValue:
return types.NullValue, nil
case *exprpb.Constant_StringValue:
return types.String(c.GetStringValue()), nil
case *exprpb.Constant_Uint64Value:
return types.Uint(c.GetUint64Value()), nil
}
return nil, fmt.Errorf("unsupported constant kind: %v", c.GetConstantKind())
}

View File

@ -0,0 +1,632 @@
// 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"
"github.com/google/cel-go/common/types"
"github.com/google/cel-go/common/types/ref"
structpb "google.golang.org/protobuf/types/known/structpb"
exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1"
)
// 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.NewComprehension(id,
iterRange,
comp.GetIterVar(),
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(),
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) {
if c == nil {
return nil, nil
}
switch c.GetConstantKind().(type) {
case *exprpb.Constant_BoolValue:
return types.Bool(c.GetBoolValue()), nil
case *exprpb.Constant_BytesValue:
return types.Bytes(c.GetBytesValue()), nil
case *exprpb.Constant_DoubleValue:
return types.Double(c.GetDoubleValue()), nil
case *exprpb.Constant_Int64Value:
return types.Int(c.GetInt64Value()), nil
case *exprpb.Constant_NullValue:
return types.NullValue, nil
case *exprpb.Constant_StringValue:
return types.String(c.GetStringValue()), nil
case *exprpb.Constant_Uint64Value:
return types.Uint(c.GetUint64Value()), nil
}
return nil, fmt.Errorf("unsupported constant kind: %v", c.GetConstantKind())
}

File diff suppressed because it is too large Load Diff

303
vendor/github.com/google/cel-go/common/ast/factory.go generated vendored Normal file
View File

@ -0,0 +1,303 @@
// 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 comprehension over a value range.
NewComprehension(id int64, iterRange Expr, iterVar, 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
// 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 indicies 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{}
// NewExprFactory creates an ExprFactory instance.
func NewExprFactory() ExprFactory {
return &baseExprFactory{}
}
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 {
return fac.newExpr(
id,
&baseComprehensionExpr{
iterRange: iterRange,
iterVar: iterVar,
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, "__result__")
}
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.NewComprehension(e.ID(),
fac.CopyExpr(compre.IterRange()),
compre.IterVar(),
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{}
)

652
vendor/github.com/google/cel-go/common/ast/navigable.go generated vendored Normal file
View File

@ -0,0 +1,652 @@
// 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() {
visitor.VisitEntryExpr(f)
visit(f.AsStructField().Value(), visitor, order, depth+1, maxDepth)
}
}
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) 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()),
}
}