Bump cel-go to v0.12.3

This commit is contained in:
Cici Huang
2022-07-13 20:19:59 +00:00
parent 51fcfb97ce
commit c67e914373
10 changed files with 249 additions and 54 deletions

View File

@@ -662,8 +662,9 @@ func (f *functionDecl) merge(other *functionDecl) (*functionDecl, error) {
return merged, nil
}
// addOverload ensures that the new overload does not collide with an existing overload signature,
// nor does it redefine an existing overload binding.
// addOverload ensures that the new overload does not collide with an existing overload signature;
// however, if the function signatures are identical, the implementation may be rewritten as its
// difficult to compare functions by object identity.
func (f *functionDecl) addOverload(overload *overloadDecl) error {
for id, o := range f.overloads {
if id != overload.id && o.signatureOverlaps(overload) {
@@ -671,11 +672,8 @@ func (f *functionDecl) addOverload(overload *overloadDecl) error {
}
if id == overload.id {
if o.signatureEquals(overload) && o.nonStrict == overload.nonStrict {
if !o.hasBinding() && overload.hasBinding() {
f.overloads[id] = overload
} else if o.hasBinding() && overload.hasBinding() && o != overload {
return fmt.Errorf("overload binding collision in function %s: %s has multiple bindings", f.name, o.id)
}
// Allow redefinition of an overload implementation so long as the signatures match.
f.overloads[id] = overload
} else {
return fmt.Errorf("overload redefinition in function. %s: %s has multiple definitions", f.name, o.id)
}

View File

@@ -94,13 +94,14 @@ func FormatType(t *exprpb.Type) string {
// Env encapsulates the context necessary to perform parsing, type checking, or generation of
// evaluable programs for different expressions.
type Env struct {
Container *containers.Container
functions map[string]*functionDecl
declarations []*exprpb.Decl
macros []parser.Macro
adapter ref.TypeAdapter
provider ref.TypeProvider
features map[int]bool
Container *containers.Container
functions map[string]*functionDecl
declarations []*exprpb.Decl
macros []parser.Macro
adapter ref.TypeAdapter
provider ref.TypeProvider
features map[int]bool
appliedFeatures map[int]bool
// Internal parser representation
prsr *parser.Parser
@@ -150,14 +151,15 @@ func NewCustomEnv(opts ...EnvOption) (*Env, error) {
return nil, err
}
return (&Env{
declarations: []*exprpb.Decl{},
functions: map[string]*functionDecl{},
macros: []parser.Macro{},
Container: containers.DefaultContainer,
adapter: registry,
provider: registry,
features: map[int]bool{},
progOpts: []ProgramOption{},
declarations: []*exprpb.Decl{},
functions: map[string]*functionDecl{},
macros: []parser.Macro{},
Container: containers.DefaultContainer,
adapter: registry,
provider: registry,
features: map[int]bool{},
appliedFeatures: map[int]bool{},
progOpts: []ProgramOption{},
}).configure(opts)
}
@@ -294,6 +296,10 @@ func (e *Env) Extend(opts ...EnvOption) (*Env, error) {
for k, v := range e.features {
featuresCopy[k] = v
}
appliedFeaturesCopy := make(map[int]bool, len(e.appliedFeatures))
for k, v := range e.appliedFeatures {
appliedFeaturesCopy[k] = v
}
funcsCopy := make(map[string]*functionDecl, len(e.functions))
for k, v := range e.functions {
funcsCopy[k] = v
@@ -301,15 +307,16 @@ func (e *Env) Extend(opts ...EnvOption) (*Env, error) {
// TODO: functions copy needs to happen here.
ext := &Env{
Container: e.Container,
declarations: decsCopy,
functions: funcsCopy,
macros: macsCopy,
progOpts: progOptsCopy,
adapter: adapter,
features: featuresCopy,
provider: provider,
chkOpts: chkOptsCopy,
Container: e.Container,
declarations: decsCopy,
functions: funcsCopy,
macros: macsCopy,
progOpts: progOptsCopy,
adapter: adapter,
features: featuresCopy,
appliedFeatures: appliedFeaturesCopy,
provider: provider,
chkOpts: chkOptsCopy,
}
return ext.configure(opts)
}
@@ -458,9 +465,15 @@ func (e *Env) configure(opts []EnvOption) (*Env, error) {
// If the default UTC timezone fix has been enabled, make sure the library is configured
if e.HasFeature(featureDefaultUTCTimeZone) {
e, err = Lib(timeUTCLibrary{})(e)
if err != nil {
return nil, err
if _, found := e.appliedFeatures[featureDefaultUTCTimeZone]; !found {
e, err = Lib(timeUTCLibrary{})(e)
if err != nil {
return nil, err
}
// record that the feature has been applied since it will generate declarations
// and functions which will be propagated on Extend() calls and which should only
// be registered once.
e.appliedFeatures[featureDefaultUTCTimeZone] = true
}
}

View File

@@ -85,7 +85,7 @@ func (stdLibrary) ProgramOptions() []ProgramOption {
type timeUTCLibrary struct{}
func (timeUTCLibrary) CompileOptions() []EnvOption {
return timestampOverloadDeclarations
return timeOverloadDeclarations
}
func (timeUTCLibrary) ProgramOptions() []ProgramOption {
@@ -97,7 +97,31 @@ func (timeUTCLibrary) ProgramOptions() []ProgramOption {
var (
utcTZ = types.String("UTC")
timestampOverloadDeclarations = []EnvOption{
timeOverloadDeclarations = []EnvOption{
Function(overloads.TimeGetHours,
MemberOverload(overloads.DurationToHours, []*Type{DurationType}, IntType,
UnaryBinding(func(dur ref.Val) ref.Val {
d := dur.(types.Duration)
return types.Int(d.Hours())
}))),
Function(overloads.TimeGetMinutes,
MemberOverload(overloads.DurationToMinutes, []*Type{DurationType}, IntType,
UnaryBinding(func(dur ref.Val) ref.Val {
d := dur.(types.Duration)
return types.Int(d.Minutes())
}))),
Function(overloads.TimeGetSeconds,
MemberOverload(overloads.DurationToSeconds, []*Type{DurationType}, IntType,
UnaryBinding(func(dur ref.Val) ref.Val {
d := dur.(types.Duration)
return types.Int(d.Seconds())
}))),
Function(overloads.TimeGetMilliseconds,
MemberOverload(overloads.DurationToMilliseconds, []*Type{DurationType}, IntType,
UnaryBinding(func(dur ref.Val) ref.Val {
d := dur.(types.Duration)
return types.Int(d.Milliseconds())
}))),
Function(overloads.TimeGetFullYear,
MemberOverload(overloads.TimestampToYear, []*Type{TimestampType}, IntType,
UnaryBinding(func(ts ref.Val) ref.Val {

View File

@@ -141,3 +141,13 @@ func Precedence(symbol string) int {
}
return op.precedence
}
// Arity returns the number of argument the operator takes
// -1 is returned if an undefined symbol is provided
func Arity(symbol string) int {
op, found := operatorMap[symbol]
if !found {
return -1
}
return op.arity
}

View File

@@ -36,9 +36,29 @@ import (
// - Floating point values are converted to the small number of digits needed to represent the value.
// - Spacing around punctuation marks may be lost.
// - Parentheses will only be applied when they affect operator precedence.
func Unparse(expr *exprpb.Expr, info *exprpb.SourceInfo) (string, error) {
un := &unparser{info: info}
err := un.visit(expr)
//
// This function optionally takes in one or more UnparserOption to alter the unparsing behavior, such as
// performing word wrapping on expressions.
func Unparse(expr *exprpb.Expr, info *exprpb.SourceInfo, opts ...UnparserOption) (string, error) {
unparserOpts := &unparserOption{
wrapOnColumn: defaultWrapOnColumn,
wrapAfterColumnLimit: defaultWrapAfterColumnLimit,
operatorsToWrapOn: defaultOperatorsToWrapOn,
}
var err error
for _, opt := range opts {
unparserOpts, err = opt(unparserOpts)
if err != nil {
return "", err
}
}
un := &unparser{
info: info,
options: unparserOpts,
}
err = un.visit(expr)
if err != nil {
return "", err
}
@@ -47,8 +67,10 @@ func Unparse(expr *exprpb.Expr, info *exprpb.SourceInfo) (string, error) {
// unparser visits an expression to reconstruct a human-readable string from an AST.
type unparser struct {
str strings.Builder
info *exprpb.SourceInfo
str strings.Builder
info *exprpb.SourceInfo
options *unparserOption
lastWrappedIndex int
}
func (un *unparser) visit(expr *exprpb.Expr) error {
@@ -135,9 +157,8 @@ func (un *unparser) visitCallBinary(expr *exprpb.Expr) error {
if !found {
return fmt.Errorf("cannot unmangle operator: %s", fun)
}
un.str.WriteString(" ")
un.str.WriteString(unmangled)
un.str.WriteString(" ")
un.writeOperatorWithWrapping(fun, unmangled)
return un.visitMaybeNested(rhs, rhsParen)
}
@@ -151,7 +172,8 @@ func (un *unparser) visitCallConditional(expr *exprpb.Expr) error {
if err != nil {
return err
}
un.str.WriteString(" ? ")
un.writeOperatorWithWrapping(operators.Conditional, "?")
// add parens if operand is a conditional itself.
nested = isSamePrecedence(operators.Conditional, args[1]) ||
isComplexOperator(args[1])
@@ -159,6 +181,7 @@ func (un *unparser) visitCallConditional(expr *exprpb.Expr) error {
if err != nil {
return err
}
un.str.WriteString(" : ")
// add parens if operand is a conditional itself.
nested = isSamePrecedence(operators.Conditional, args[2]) ||
@@ -444,3 +467,130 @@ func bytesToOctets(byteVal []byte) string {
}
return b.String()
}
// writeOperatorWithWrapping outputs the operator and inserts a newline for operators configured
// in the unparser options.
func (un *unparser) writeOperatorWithWrapping(fun string, unmangled string) bool {
_, wrapOperatorExists := un.options.operatorsToWrapOn[fun]
lineLength := un.str.Len() - un.lastWrappedIndex + len(fun)
if wrapOperatorExists && lineLength >= un.options.wrapOnColumn {
un.lastWrappedIndex = un.str.Len()
// wrapAfterColumnLimit flag dictates whether the newline is placed
// before or after the operator
if un.options.wrapAfterColumnLimit {
// Input: a && b
// Output: a &&\nb
un.str.WriteString(" ")
un.str.WriteString(unmangled)
un.str.WriteString("\n")
} else {
// Input: a && b
// Output: a\n&& b
un.str.WriteString("\n")
un.str.WriteString(unmangled)
un.str.WriteString(" ")
}
return true
} else {
un.str.WriteString(" ")
un.str.WriteString(unmangled)
un.str.WriteString(" ")
}
return false
}
// Defined defaults for the unparser options
var (
defaultWrapOnColumn = 80
defaultWrapAfterColumnLimit = true
defaultOperatorsToWrapOn = map[string]bool{
operators.LogicalAnd: true,
operators.LogicalOr: true,
}
)
// UnparserOption is a functional option for configuring the output formatting
// of the Unparse function.
type UnparserOption func(*unparserOption) (*unparserOption, error)
// Internal representation of the UnparserOption type
type unparserOption struct {
wrapOnColumn int
operatorsToWrapOn map[string]bool
wrapAfterColumnLimit bool
}
// WrapOnColumn wraps the output expression when its string length exceeds a specified limit
// for operators set by WrapOnOperators function or by default, "&&" and "||" will be wrapped.
//
// Example usage:
//
// Unparse(expr, sourceInfo, WrapOnColumn(40), WrapOnOperators(Operators.LogicalAnd))
//
// This will insert a newline immediately after the logical AND operator for the below example input:
//
// Input:
// 'my-principal-group' in request.auth.claims && request.auth.claims.iat > now - duration('5m')
//
// Output:
// 'my-principal-group' in request.auth.claims &&
// request.auth.claims.iat > now - duration('5m')
func WrapOnColumn(col int) UnparserOption {
return func(opt *unparserOption) (*unparserOption, error) {
if col < 1 {
return nil, fmt.Errorf("Invalid unparser option. Wrap column value must be greater than or equal to 1. Got %v instead", col)
}
opt.wrapOnColumn = col
return opt, nil
}
}
// WrapOnOperators specifies which operators to perform word wrapping on an output expression when its string length
// exceeds the column limit set by WrapOnColumn function.
//
// Word wrapping is supported on non-unary symbolic operators. Refer to operators.go for the full list
//
// This will replace any previously supplied operators instead of merging them.
func WrapOnOperators(symbols ...string) UnparserOption {
return func(opt *unparserOption) (*unparserOption, error) {
opt.operatorsToWrapOn = make(map[string]bool)
for _, symbol := range symbols {
_, found := operators.FindReverse(symbol)
if !found {
return nil, fmt.Errorf("Invalid unparser option. Unsupported operator: %s", symbol)
}
arity := operators.Arity(symbol)
if arity < 2 {
return nil, fmt.Errorf("Invalid unparser option. Unary operators are unsupported: %s", symbol)
}
opt.operatorsToWrapOn[symbol] = true
}
return opt, nil
}
}
// WrapAfterColumnLimit dictates whether to insert a newline before or after the specified operator
// when word wrapping is performed.
//
// Example usage:
//
// Unparse(expr, sourceInfo, WrapOnColumn(40), WrapOnOperators(Operators.LogicalAnd), WrapAfterColumnLimit(false))
//
// This will insert a newline immediately before the logical AND operator for the below example input, ensuring
// that the length of a line never exceeds the specified column limit:
//
// Input:
// 'my-principal-group' in request.auth.claims && request.auth.claims.iat > now - duration('5m')
//
// Output:
// 'my-principal-group' in request.auth.claims
// && request.auth.claims.iat > now - duration('5m')
func WrapAfterColumnLimit(wrapAfter bool) UnparserOption {
return func(opt *unparserOption) (*unparserOption, error) {
opt.wrapAfterColumnLimit = wrapAfter
return opt, nil
}
}