From 38484af293725aaa62806480ec6d67b54a8097ac Mon Sep 17 00:00:00 2001 From: Sokwhan Huh Date: Tue, 16 Apr 2024 11:14:55 -0700 Subject: [PATCH] Introduce a common expression tree interface between CelExpr and CelMutableExpr PiperOrigin-RevId: 625393691 --- .../main/java/dev/cel/common/ast/BUILD.bazel | 1 + .../main/java/dev/cel/common/ast/CelExpr.java | 235 +++++---------- .../dev/cel/common/ast/CelMutableExpr.java | 93 ++++-- .../java/dev/cel/common/ast/Expression.java | 280 ++++++++++++++++++ 4 files changed, 413 insertions(+), 196 deletions(-) create mode 100644 common/src/main/java/dev/cel/common/ast/Expression.java diff --git a/common/src/main/java/dev/cel/common/ast/BUILD.bazel b/common/src/main/java/dev/cel/common/ast/BUILD.bazel index b2618130..ac4df548 100644 --- a/common/src/main/java/dev/cel/common/ast/BUILD.bazel +++ b/common/src/main/java/dev/cel/common/ast/BUILD.bazel @@ -13,6 +13,7 @@ AST_SOURCES = [ "CelExpr.java", "CelExprFormatter.java", "CelReference.java", + "Expression.java", ] # keep sorted diff --git a/common/src/main/java/dev/cel/common/ast/CelExpr.java b/common/src/main/java/dev/cel/common/ast/CelExpr.java index f494aa11..3970c859 100644 --- a/common/src/main/java/dev/cel/common/ast/CelExpr.java +++ b/common/src/main/java/dev/cel/common/ast/CelExpr.java @@ -31,104 +31,102 @@ import java.util.Optional; /** - * An abstract representation of a common expression. + * An abstract representation of a common expression. Refer to {@link Expression} for details. * *

This is the native type equivalent of Expr message in syntax.proto. - * - *

Expressions are abstractly represented as a collection of identifiers, select statements, - * function calls, literals, and comprehensions. All operators with the exception of the '.' - * operator are modelled as function calls. This makes it easy to represent new operators into the - * existing AST. - * - *

All references within expressions must resolve to a [Decl][] provided at type-check for an - * expression to be valid. A reference may either be a bare identifier `name` or a qualified - * identifier `google.api.name`. References may either refer to a value or a function declaration. - * - *

For example, the expression `google.api.name.startsWith('expr')` references the declaration - * `google.api.name` within a [Expr.Select][] expression, and the function declaration `startsWith`. */ @AutoValue @Internal @Immutable -public abstract class CelExpr { +public abstract class CelExpr implements Expression { - /** - * Required. An id assigned to this node by the parser which is unique in a given expression tree. - * This is used to associate type information and other attributes to a node in the parse tree. - */ + @Override public abstract long id(); /** Represents the variant of the expression. */ public abstract ExprKind exprKind(); + @Override + public ExprKind.Kind getKind() { + return exprKind().getKind(); + } + /** - * Gets the underlying constant expression. + * {@inheritDoc} * * @throws UnsupportedOperationException if expression is not {@link Kind#CONSTANT}. */ + @Override public CelConstant constant() { return exprKind().constant(); } /** - * Gets the underlying identifier expression. + * {@inheritDoc} * * @throws UnsupportedOperationException if expression is not {@link Kind#IDENT}. */ + @Override public CelIdent ident() { return exprKind().ident(); } /** - * Gets the underlying select expression. + * {@inheritDoc} * * @throws UnsupportedOperationException if expression is not {@link Kind#SELECT}. */ + @Override public CelSelect select() { return exprKind().select(); } /** - * Gets the underlying call expression. + * {@inheritDoc} * * @throws UnsupportedOperationException if expression is not {@link Kind#CALL}. */ + @Override public CelCall call() { return exprKind().call(); } /** - * Gets the underlying createList expression. + * {@inheritDoc} * * @throws UnsupportedOperationException if expression is not {@link Kind#CREATE_LIST}. */ + @Override public CelCreateList createList() { return exprKind().createList(); } /** - * Gets the underlying createStruct expression. + * {@inheritDoc} * * @throws UnsupportedOperationException if expression is not {@link Kind#CREATE_STRUCT}. */ + @Override public CelCreateStruct createStruct() { return exprKind().createStruct(); } /** - * Gets the underlying createMap expression. + * {@inheritDoc} * * @throws UnsupportedOperationException if expression is not {@link Kind#createMap}. */ + @Override public CelCreateMap createMap() { return exprKind().createMap(); } /** - * Gets the underlying comprehension expression. + * {@inheritDoc} * * @throws UnsupportedOperationException if expression is not {@link Kind#COMPREHENSION}. */ + @Override public CelComprehension comprehension() { return exprKind().comprehension(); } @@ -401,12 +399,9 @@ public abstract static class CelNotSet {} /** An identifier expression. e.g. `request`. */ @AutoValue @Immutable - public abstract static class CelIdent { - /** - * Required. Holds a single, unqualified identifier, possibly preceded by a '.'. - * - *

Qualified names are represented by the [Expr.Select][] expression. - */ + public abstract static class CelIdent implements Ident { + + @Override public abstract String name(); /** Builder for CelIdent. */ @@ -429,29 +424,15 @@ public static Builder newBuilder() { /** A field selection expression. e.g. `request.auth`. */ @AutoValue @Immutable - public abstract static class CelSelect { + public abstract static class CelSelect implements Expression.Select { - /** - * Required. The target of the selection expression. - * - *

For example, in the select expression `request.auth`, the `request` portion of the - * expression is the `operand`. - */ + @Override public abstract CelExpr operand(); - /** - * Required. The name of the field to select. - * - *

For example, in the select expression `request.auth`, the `auth` portion of the expression - * would be the `field`. - */ + @Override public abstract String field(); - /** - * Whether the select is to be interpreted as a field presence test. - * - *

This results from the macro `has(request.auth)`. - */ + @Override public abstract boolean testOnly(); /** Builder for CelSelect. */ @@ -483,25 +464,18 @@ public static Builder newBuilder() { } } - /** - * A call expression, including calls to predefined functions and operators. - * - *

For example, `value == 10`, `size(map_value)`. - */ + /** A call expression. See {@link Expression.Call} */ @AutoValue @Immutable - public abstract static class CelCall { + public abstract static class CelCall implements Expression.Call { - /** - * The target of a method call-style expression. - * - *

For example, `x` in `x.f()`. - */ + @Override public abstract Optional target(); - /** Required. The name of the function or method being called. */ + @Override public abstract String function(); + @Override public abstract ImmutableList args(); /** Builder for CelCall. */ @@ -587,24 +561,14 @@ public static Builder newBuilder() { } } - /** - * A list creation expression. - * - *

Lists may either be homogenous, e.g. `[1, 2, 3]`, or heterogeneous, e.g. `dyn([1, 'hello', - * 2.0])` - */ + /** A list creation expression. See {@link Expression.CreateList} */ @AutoValue @Immutable - public abstract static class CelCreateList { - /** The elements part of the list */ + public abstract static class CelCreateList implements Expression.CreateList { + @Override public abstract ImmutableList elements(); - /** - * The indices within the elements list which are marked as optional elements. - * - *

When an optional-typed value is present, the value it contains is included in the list. If - * the optional-typed value is absent, the list element is omitted from the CreateList result. - */ + @Override public abstract ImmutableList optionalIndices(); /** Builder for CelCreateList. */ @@ -689,19 +653,15 @@ public static Builder newBuilder() { } } - /** - * A message creation expression. - * - *

Messages are constructed with a type name and composed of field ids: `types.MyType{field_id: - * 'value'}`. - */ + /** A message creation expression. See {@link Expression.CreateStruct} */ @AutoValue @Immutable - public abstract static class CelCreateStruct { - /** The type name of the message to be created, empty when creating map literals. */ + public abstract static class CelCreateStruct + implements Expression.CreateStruct { + @Override public abstract String messageName(); - /** The entries in the creation expression. */ + @Override public abstract ImmutableList entries(); /** Builder for CelCreateStruct. */ @@ -777,26 +737,18 @@ public static Builder newBuilder() { /** Represents an entry of the struct */ @AutoValue @Immutable - public abstract static class Entry { - /** - * Required. An id assigned to this node by the parser which is unique in a given expression - * tree. This is used to associate type information and other attributes to the node. - */ + public abstract static class Entry implements Expression.CreateStruct.Entry { + + @Override public abstract long id(); - /** Entry key kind. */ + @Override public abstract String fieldKey(); - /** - * Required. The value assigned to the key. - * - *

If the optional_entry field is true, the expression must resolve to an optional-typed - * value. If the optional value is present, the key will be set; however, if the optional - * value is absent, the key will be unset. - */ + @Override public abstract CelExpr value(); - /** Whether the key-value pair is optional. */ + @Override public abstract boolean optionalEntry(); /** Builder for CelCreateStruct.Entry. */ @@ -829,15 +781,12 @@ public static Builder newBuilder() { } } - /** - * A map creation expression. - * - *

Maps are constructed as `{'key_name': 'value'}`. - */ + /** A map creation expression. See {@link Expression.CreateMap} */ @AutoValue @Immutable - public abstract static class CelCreateMap { + public abstract static class CelCreateMap implements Expression.CreateMap { /** The entries in the creation expression. */ + @Override public abstract ImmutableList entries(); /** Builder for CelCreateMap. */ @@ -908,29 +857,21 @@ public static Builder newBuilder() { return new AutoValue_CelExpr_CelCreateMap.Builder(); } - /** Represents an entry of the map */ + /** Represents an entry of the map. */ @AutoValue @Immutable - public abstract static class Entry { - /** - * Required. An id assigned to this node by the parser which is unique in a given expression - * tree. This is used to associate type information and other attributes to the node. - */ + public abstract static class Entry implements Expression.CreateMap.Entry { + + @Override public abstract long id(); - /** Required. The key. */ + @Override public abstract CelExpr key(); - /** - * Required. The value assigned to the key. - * - *

If the optional_entry field is true, the expression must resolve to an optional-typed - * value. If the optional value is present, the key will be set; however, if the optional - * value is absent, the key will be unset. - */ + @Override public abstract CelExpr value(); - /** Whether the key-value pair is optional. */ + @Override public abstract boolean optionalEntry(); /** Builder for CelCreateMap.Entry. */ @@ -962,65 +903,29 @@ public static CelCreateMap.Entry.Builder newBuilder() { } } - /** - * A comprehension expression applied to a list or map. - * - *

Comprehensions are not part of the core syntax, but enabled with macros. A macro matches a - * specific call signature within a parsed AST and replaces the call with an alternate AST block. - * Macro expansion happens at parse time. - * - *

The following macros are supported within CEL: - * - *

Aggregate type macros may be applied to all elements in a list or all keys in a map: - * - *

`all`, `exists`, `exists_one` - test a predicate expression against the inputs and return - * `true` if the predicate is satisfied for all, any, or only one value `list.all(x, x < 10)`. - * `filter` - test a predicate expression against the inputs and return the subset of elements - * which satisfy the predicate: `payments.filter(p, p > 1000)`. `map` - apply an expression to all - * elements in the input and return the output aggregate type: `[1, 2, 3].map(i, i * i)`. - * - *

The `has(m.x)` macro tests whether the property `x` is present in struct `m`. The semantics - * of this macro depend on the type of `m`. For proto2 messages `has(m.x)` is defined as 'defined, - * but not set`. For proto3, the macro tests whether the property is set to its default. For map - * and struct types, the macro tests whether the property `x` is defined on `m`. - * - *

Comprehension evaluation can be best visualized as the following pseudocode: - */ + /** A comprehension expression applied to a list or map. See {@link Expression.Comprehension} */ @AutoValue @Immutable - public abstract static class CelComprehension { - /** The name of the iteration variable. */ + public abstract static class CelComprehension implements Expression.Comprehension { + @Override public abstract String iterVar(); - /** The range over which var iterates. */ + @Override public abstract CelExpr iterRange(); - /** The name of the variable used for accumulation of the result. */ + @Override public abstract String accuVar(); - /** The initial value of the accumulator. */ + @Override public abstract CelExpr accuInit(); - /** - * An expression which can contain iter_var and accu_var. - * - *

Returns false when the result has been computed and may be used as a hint to short-circuit - * the remainder of the comprehension. - */ + @Override public abstract CelExpr loopCondition(); - /** - * An expression which can contain iter_var and accu_var. - * - *

Computes the next value of accu_var. - */ + @Override public abstract CelExpr loopStep(); - /** - * An expression which can contain accu_var. - * - *

Computes the result. - */ + @Override public abstract CelExpr result(); /** Builder for Comprehension. */ diff --git a/common/src/main/java/dev/cel/common/ast/CelMutableExpr.java b/common/src/main/java/dev/cel/common/ast/CelMutableExpr.java index 7b84826f..0a44f423 100644 --- a/common/src/main/java/dev/cel/common/ast/CelMutableExpr.java +++ b/common/src/main/java/dev/cel/common/ast/CelMutableExpr.java @@ -28,18 +28,21 @@ /** * An abstract representation of a common expression that allows mutation in any of its properties. - * The expressions are semantically the same as that of the immutable {@link CelExpr}. + * The expressions are semantically the same as that of the immutable {@link CelExpr}. Refer to + * {@link Expression} for details. * *

This allows for an efficient optimization of an AST without having to traverse and rebuild the * entire tree. * *

This class is not thread-safe by design. */ -public final class CelMutableExpr { +@SuppressWarnings("unchecked") // Class ensures only the super type is used +public final class CelMutableExpr implements Expression { private long id; private ExprKind.Kind exprKind; private Object exprValue; + @Override public long id() { return id; } @@ -48,6 +51,7 @@ public void setId(long id) { this.id = id; } + @Override public ExprKind.Kind getKind() { return exprKind; } @@ -57,41 +61,49 @@ public CelNotSet notSet() { return (CelNotSet) exprValue; } + @Override public CelConstant constant() { checkExprKind(Kind.CONSTANT); return (CelConstant) exprValue; } + @Override public CelMutableIdent ident() { checkExprKind(Kind.IDENT); return (CelMutableIdent) exprValue; } + @Override public CelMutableSelect select() { checkExprKind(Kind.SELECT); return (CelMutableSelect) exprValue; } + @Override public CelMutableCall call() { checkExprKind(Kind.CALL); return (CelMutableCall) exprValue; } + @Override public CelMutableCreateList createList() { checkExprKind(Kind.CREATE_LIST); return (CelMutableCreateList) exprValue; } + @Override public CelMutableCreateStruct createStruct() { checkExprKind(Kind.CREATE_STRUCT); return (CelMutableCreateStruct) exprValue; } + @Override public CelMutableCreateMap createMap() { checkExprKind(Kind.CREATE_MAP); return (CelMutableCreateMap) exprValue; } + @Override public CelMutableComprehension comprehension() { checkExprKind(Kind.COMPREHENSION); return (CelMutableComprehension) exprValue; @@ -138,9 +150,10 @@ public void setComprehension(CelMutableComprehension comprehension) { } /** A mutable identifier expression. */ - public static final class CelMutableIdent { + public static final class CelMutableIdent implements Ident { private String name = ""; + @Override public String name() { return name; } @@ -181,11 +194,12 @@ private CelMutableIdent(String name) { } /** A mutable field selection expression. e.g. `request.auth`. */ - public static final class CelMutableSelect { + public static final class CelMutableSelect implements Expression.Select { private CelMutableExpr operand; private String field = ""; private boolean testOnly; + @Override public CelMutableExpr operand() { return operand; } @@ -194,6 +208,7 @@ public void setOperand(CelMutableExpr operand) { this.operand = checkNotNull(operand); } + @Override public String field() { return field; } @@ -202,6 +217,7 @@ public void setField(String field) { this.field = checkNotNull(field); } + @Override public boolean testOnly() { return testOnly; } @@ -255,12 +271,13 @@ private CelMutableSelect(CelMutableExpr operand, String field, boolean testOnly) } } - /** A mutable call expression, including calls to predefined functions and operators. */ - public static final class CelMutableCall { + /** A mutable call expression. See {@link Expression.Call} */ + public static final class CelMutableCall implements Expression.Call { private Optional target; private String function; private List args; + @Override public Optional target() { return target; } @@ -269,6 +286,7 @@ public void setTarget(CelMutableExpr target) { this.target = Optional.of(target); } + @Override public String function() { return function; } @@ -277,6 +295,7 @@ public void setFunction(String function) { this.function = checkNotNull(function); } + @Override public List args() { return args; } @@ -366,16 +385,12 @@ private CelMutableCall(CelMutableExpr target, String function, ListLists may either be homogenous, e.g. `[1, 2, 3]`, or heterogeneous, e.g. `dyn([1, 'hello', - * 2.0])` - */ - public static final class CelMutableCreateList { + /** A mutable list creation expression. See {@link Expression.CreateList} */ + public static final class CelMutableCreateList implements Expression.CreateList { private final List elements; private final List optionalIndices; + @Override public List elements() { return elements; } @@ -385,6 +400,7 @@ public void setElement(int index, CelMutableExpr element) { elements.set(index, checkNotNull(element)); } + @Override public List optionalIndices() { return optionalIndices; } @@ -438,16 +454,13 @@ private CelMutableCreateList( } } - /** - * A mutable list creation expression. - * - *

Lists may either be homogenous, e.g. `[1, 2, 3]`, or heterogeneous, e.g. `dyn([1, 'hello', - * 2.0])` - */ - public static final class CelMutableCreateStruct { + /** A mutable list creation expression. See {@link Expression.CreateStruct} */ + public static final class CelMutableCreateStruct + implements Expression.CreateStruct { private String messageName = ""; private List entries; + @Override public String messageName() { return messageName; } @@ -456,6 +469,7 @@ public void setMessageName(String messageName) { this.messageName = checkNotNull(messageName); } + @Override public List entries() { return entries; } @@ -469,13 +483,14 @@ public void setEntry(int index, CelMutableCreateStruct.Entry entry) { entries.set(index, checkNotNull(entry)); } - /** Represents a mutable entry of the struct */ - public static final class Entry { + /** Represents a mutable entry of the struct. */ + public static final class Entry implements Expression.CreateStruct.Entry { private long id; private String fieldKey = ""; private CelMutableExpr value; private boolean optionalEntry; + @Override public long id() { return id; } @@ -484,6 +499,7 @@ public void setId(long id) { this.id = id; } + @Override public String fieldKey() { return fieldKey; } @@ -492,6 +508,7 @@ public void setFieldKey(String fieldKey) { this.fieldKey = checkNotNull(fieldKey); } + @Override public CelMutableExpr value() { return value; } @@ -500,6 +517,7 @@ public void setValue(CelMutableExpr value) { this.value = checkNotNull(value); } + @Override public boolean optionalEntry() { return optionalEntry; } @@ -600,14 +618,12 @@ private CelMutableCreateStruct(String messageName, ListMaps are constructed as `{'key_name': 'value'}`. - */ - public static final class CelMutableCreateMap { + /** A mutable map creation expression. See {@link Expression.CreateMap} */ + public static final class CelMutableCreateMap + implements Expression.CreateMap { private List entries; + @Override public List entries() { return entries; } @@ -622,12 +638,13 @@ public void setEntry(int index, CelMutableCreateMap.Entry entry) { } /** Represents an entry of the map */ - public static final class Entry { + public static final class Entry implements Expression.CreateMap.Entry { private long id; private CelMutableExpr key; private CelMutableExpr value; private boolean optionalEntry; + @Override public long id() { return id; } @@ -636,6 +653,7 @@ public void setId(long id) { this.id = id; } + @Override public CelMutableExpr key() { return key; } @@ -644,6 +662,7 @@ public void setKey(CelMutableExpr key) { this.key = checkNotNull(key); } + @Override public CelMutableExpr value() { return value; } @@ -652,6 +671,7 @@ public void setValue(CelMutableExpr value) { this.value = checkNotNull(value); } + @Override public boolean optionalEntry() { return optionalEntry; } @@ -752,8 +772,12 @@ private CelMutableCreateMap(List entries) { } } - /** A mutable comprehension expression applied to a list or map. */ - public static final class CelMutableComprehension { + /** + * A mutable comprehension expression applied to a list or map. See {@link + * Expression.Comprehension} + */ + public static final class CelMutableComprehension + implements Expression.Comprehension { private String iterVar; @@ -769,6 +793,7 @@ public static final class CelMutableComprehension { private CelMutableExpr result; + @Override public String iterVar() { return iterVar; } @@ -777,6 +802,7 @@ public void setIterVar(String iterVar) { this.iterVar = checkNotNull(iterVar); } + @Override public CelMutableExpr iterRange() { return iterRange; } @@ -785,6 +811,7 @@ public void setIterRange(CelMutableExpr iterRange) { this.iterRange = checkNotNull(iterRange); } + @Override public String accuVar() { return accuVar; } @@ -793,6 +820,7 @@ public void setAccuVar(String accuVar) { this.accuVar = checkNotNull(accuVar); } + @Override public CelMutableExpr accuInit() { return accuInit; } @@ -801,6 +829,7 @@ public void setAccuInit(CelMutableExpr accuInit) { this.accuInit = checkNotNull(accuInit); } + @Override public CelMutableExpr loopCondition() { return loopCondition; } @@ -809,6 +838,7 @@ public void setLoopCondition(CelMutableExpr loopCondition) { this.loopCondition = checkNotNull(loopCondition); } + @Override public CelMutableExpr loopStep() { return loopStep; } @@ -817,6 +847,7 @@ public void setLoopStep(CelMutableExpr loopStep) { this.loopStep = checkNotNull(loopStep); } + @Override public CelMutableExpr result() { return result; } diff --git a/common/src/main/java/dev/cel/common/ast/Expression.java b/common/src/main/java/dev/cel/common/ast/Expression.java new file mode 100644 index 00000000..3c2f7a4e --- /dev/null +++ b/common/src/main/java/dev/cel/common/ast/Expression.java @@ -0,0 +1,280 @@ +// Copyright 2024 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package dev.cel.common.ast; + +import dev.cel.common.annotations.Internal; +import java.util.List; +import java.util.Optional; + +/** + * An abstract representation of a common expression. + * + *

Expressions are abstractly represented as a collection of identifiers, select statements, + * function calls, literals, and comprehensions. All operators with the exception of the '.' + * operator are modelled as function calls. This makes it easy to represent new operators into the + * existing AST. + * + *

All references within expressions must resolve to a [Decl][] provided at type-check for an + * expression to be valid. A reference may either be a bare identifier `name` or a qualified + * identifier `google.api.name`. References may either refer to a value or a function declaration. + * + *

For example, the expression `google.api.name.startsWith('expr')` references the declaration + * `google.api.name` within a [Expr.Select][] expression, and the function declaration `startsWith`. + */ +@Internal +public interface Expression { + + /** + * Required. An id assigned to this node by the parser which is unique in a given expression tree. + * This is used to associate type information and other attributes to a node in the parse tree. + */ + long id(); + + /** Represents the enumeration value for the underlying expression kind. */ + CelExpr.ExprKind.Kind getKind(); + + /** Gets the underlying constant expression. */ + CelConstant constant(); + + /** Gets the underlying identifier expression. */ + Ident ident(); + + /** Gets the underlying call expression. */ + Call call(); + + /** Gets the underlying identifier expression. */ + CreateList createList(); + + /** Gets the underlying select expression. */ + Select select(); + + /** Gets the underlying createStruct expression. */ + CreateStruct> createStruct(); + + /** Gets the underlying createMap expression. */ + CreateMap> createMap(); + + /** Gets the underlying comprehension expression. */ + Comprehension comprehension(); + + /** An identifier expression. e.g. `request`. */ + interface Ident { + + /** + * Required. Holds a single, unqualified identifier, possibly preceded by a '.'. + * + *

Qualified names are represented by the [Expr.Select][] expression. + */ + String name(); + } + + /** A call expression, including calls to predefined functions and operators. */ + interface Call { + /** + * The target of a method call-style expression. + * + *

For example, `x` in `x.f()`. + */ + Optional target(); + + /** Required. The name of the function or method being called. */ + String function(); + + /** + * Arguments to the call. + * + *

For example, `foo` in `f(foo)` or `x.f(foo)`. + */ + List args(); + } + + /** + * A list creation expression. + * + *

Lists may either be homogenous, e.g. `[1, 2, 3]`, or heterogeneous, e.g. `dyn([1, 'hello', + * 2.0])` + */ + interface CreateList { + + /** The elements part of the list */ + List elements(); + + /** + * The indices within the elements list which are marked as optional elements. + * + *

When an optional-typed value is present, the value it contains is included in the list. If + * the optional-typed value is absent, the list element is omitted from the CreateList result. + */ + List optionalIndices(); + } + + /** A field selection expression. e.g. `request.auth`. */ + interface Select { + /** + * Required. The target of the selection expression. + * + *

For example, in the select expression `request.auth`, the `request` portion of the + * expression is the `operand`. + */ + E operand(); + + /** + * Required. The name of the field to select. + * + *

For example, in the select expression `request.auth`, the `auth` portion of the expression + * would be the `field`. + */ + String field(); + + /** + * Whether the select is to be interpreted as a field presence test. + * + *

This results from the macro `has(request.auth)`. + */ + boolean testOnly(); + } + + /** + * A message creation expression. + * + *

Messages are constructed with a type name and composed of field ids: `types.MyType{field_id: + * 'value'}`. + */ + interface CreateStruct> { + + /** The type name of the message to be created, empty when creating map literals. */ + String messageName(); + + /** The entries in the creation expression. */ + List entries(); + + /** Represents an entry of the struct */ + interface Entry { + /** + * Required. An id assigned to this node by the parser which is unique in a given expression + * tree. This is used to associate type information and other attributes to the node. + */ + long id(); + + /** Entry key kind. */ + String fieldKey(); + + /** + * Required. The value assigned to the key. + * + *

If the optional_entry field is true, the expression must resolve to an optional-typed + * value. If the optional value is present, the key will be set; however, if the optional + * value is absent, the key will be unset. + */ + T value(); + + /** Whether the key-value pair is optional. */ + boolean optionalEntry(); + } + } + + /** + * A map creation expression. + * + *

Maps are constructed as `{'key_name': 'value'}`. + */ + interface CreateMap> { + + List entries(); + + /** Represents an entry of the map. */ + interface Entry { + /** + * Required. An id assigned to this node by the parser which is unique in a given expression + * tree. This is used to associate type information and other attributes to the node. + */ + long id(); + + /** Required. The key. */ + T key(); + + /** + * Required. The value assigned to the key. + * + *

If the optional_entry field is true, the expression must resolve to an optional-typed + * value. If the optional value is present, the key will be set; however, if the optional + * value is absent, the key will be unset. + */ + T value(); + + boolean optionalEntry(); + } + } + + /** + * A comprehension expression applied to a list or map. + * + *

Comprehensions are not part of the core syntax, but enabled with macros. A macro matches a + * specific call signature within a parsed AST and replaces the call with an alternate AST block. + * Macro expansion happens at parse time. + * + *

The following macros are supported within CEL: + * + *

Aggregate type macros may be applied to all elements in a list or all keys in a map: + * + *

`all`, `exists`, `exists_one` - test a predicate expression against the inputs and return + * `true` if the predicate is satisfied for all, any, or only one value `list.all(x, x < 10)`. + * `filter` - test a predicate expression against the inputs and return the subset of elements + * which satisfy the predicate: `payments.filter(p, p > 1000)`. `map` - apply an expression to all + * elements in the input and return the output aggregate type: `[1, 2, 3].map(i, i * i)`. + * + *

The `has(m.x)` macro tests whether the property `x` is present in struct `m`. The semantics + * of this macro depend on the type of `m`. For proto2 messages `has(m.x)` is defined as 'defined, + * but not set`. For proto3, the macro tests whether the property is set to its default. For map + * and struct types, the macro tests whether the property `x` is defined on `m`. + * + *

Comprehension evaluation can be best visualized as the following pseudocode: + */ + interface Comprehension { + /** The name of the iteration variable. */ + String iterVar(); + + /** The range over which var iterates. */ + E iterRange(); + + /** The name of the variable used for accumulation of the result. */ + String accuVar(); + + /** The initial value of the accumulator. */ + E accuInit(); + + /** + * An expression which can contain iter_var and accu_var. + * + *

Returns false when the result has been computed and may be used as a hint to short-circuit + * the remainder of the comprehension. + */ + E loopCondition(); + + /** + * An expression which can contain iter_var and accu_var. + * + *

Computes the next value of accu_var. + */ + E loopStep(); + + /** + * An expression which can contain accu_var. + * + *

Computes the result. + */ + E result(); + } +}