|
15 | 15 | #ifndef THIRD_PARTY_CEL_CPP_COMMON_AST_H_ |
16 | 16 | #define THIRD_PARTY_CEL_CPP_COMMON_AST_H_ |
17 | 17 |
|
| 18 | +#include <cstdint> |
| 19 | +#include <string> |
| 20 | +#include <utility> |
| 21 | + |
| 22 | +#include "absl/base/nullability.h" |
| 23 | +#include "absl/container/flat_hash_map.h" |
| 24 | +#include "absl/strings/string_view.h" |
| 25 | +#include "common/ast/metadata.h" // IWYU pragma: export |
18 | 26 | #include "common/expr.h" |
19 | 27 |
|
20 | 28 | namespace cel { |
21 | 29 |
|
22 | 30 | namespace ast_internal { |
23 | | -// Forward declare supported implementations. |
24 | 31 | class AstImpl; |
25 | 32 | } // namespace ast_internal |
26 | 33 |
|
27 | | -// Runtime representation of a CEL expression's Abstract Syntax Tree. |
| 34 | +// In memory representation of a CEL abstract syntax tree. |
| 35 | +// |
| 36 | +// If AST inspection or manipulation is needed, prefer to use an existing tool |
| 37 | +// or traverse the protobuf representation rather than directly manipulating |
| 38 | +// through this class. See `cel::NavigableAst` and `cel::AstTraverse`. |
28 | 39 | // |
29 | | -// This class provides public APIs for CEL users and allows for clients to |
30 | | -// manage lifecycle. |
| 40 | +// Type and reference maps are only populated if the AST is checked. Any changes |
| 41 | +// to the AST are not automatically reflected in the type or reference maps. |
31 | 42 | // |
32 | | -// Implementations are intentionally opaque to prevent dependencies on the |
33 | | -// details of the runtime representation. To create a new instance, from a |
34 | | -// protobuf representation, use the conversion utilities in |
35 | | -// `extensions/protobuf/ast_converters.h`. |
| 43 | +// To create a new instance from a protobuf representation, use the conversion |
| 44 | +// utilities in `common/ast_proto.h`. |
36 | 45 | class Ast { |
37 | 46 | public: |
| 47 | + using ReferenceMap = absl::flat_hash_map<int64_t, Reference>; |
| 48 | + using TypeMap = absl::flat_hash_map<int64_t, TypeSpec>; |
| 49 | + |
38 | 50 | virtual ~Ast() = default; |
39 | 51 |
|
40 | | - // Whether the AST includes type check information. |
41 | | - // If false, the runtime assumes all types are dyn, and that qualified names |
42 | | - // have not been resolved. |
43 | | - virtual bool IsChecked() const = 0; |
| 52 | + Ast() : is_checked_(false) {} |
| 53 | + |
| 54 | + Ast(Expr expr, SourceInfo source_info) |
| 55 | + : root_expr_(std::move(expr)), |
| 56 | + source_info_(std::move(source_info)), |
| 57 | + is_checked_(false) {} |
| 58 | + |
| 59 | + Ast(Expr expr, SourceInfo source_info, ReferenceMap reference_map, |
| 60 | + TypeMap type_map, std::string expr_version) |
| 61 | + : root_expr_(std::move(expr)), |
| 62 | + source_info_(std::move(source_info)), |
| 63 | + reference_map_(std::move(reference_map)), |
| 64 | + type_map_(std::move(type_map)), |
| 65 | + expr_version_(std::move(expr_version)), |
| 66 | + is_checked_(true) {} |
| 67 | + |
| 68 | + // Move-only |
| 69 | + Ast(const Ast& other) = delete; |
| 70 | + Ast& operator=(const Ast& other) = delete; |
| 71 | + Ast(Ast&& other) = default; |
| 72 | + Ast& operator=(Ast&& other) = default; |
| 73 | + |
| 74 | + // Deprecated. Use `is_checked()` instead. |
| 75 | + bool IsChecked() const { return is_checked_; } |
| 76 | + |
| 77 | + bool is_checked() const { return is_checked_; } |
| 78 | + void set_is_checked(bool is_checked) { is_checked_ = is_checked; } |
| 79 | + |
| 80 | + // The root expression of the AST. |
| 81 | + // |
| 82 | + // This is the entry point for evaluation and determines the overall result |
| 83 | + // of the expression given a context. |
| 84 | + const Expr& root_expr() const { return root_expr_; } |
| 85 | + Expr& mutable_root_expr() { return root_expr_; } |
| 86 | + |
| 87 | + // Metadata about the source expression. |
| 88 | + const SourceInfo& source_info() const { return source_info_; } |
| 89 | + SourceInfo& mutable_source_info() { return source_info_; } |
| 90 | + |
| 91 | + // Returns the type of the expression with the given `expr_id`. |
| 92 | + // |
| 93 | + // Returns `nullptr` if the expression node is not found or has dynamic type. |
| 94 | + const TypeSpec* absl_nullable GetType(int64_t expr_id) const; |
| 95 | + const TypeSpec& GetTypeOrDyn(int64_t expr_id) const; |
| 96 | + const TypeSpec& GetReturnType() const; |
| 97 | + |
| 98 | + // Returns the resolved reference for the expression with the given `expr_id`. |
| 99 | + // |
| 100 | + // Returns `nullptr` if the expression node is not found or no reference was |
| 101 | + // resolved. |
| 102 | + const Reference* absl_nullable GetReference(int64_t expr_id) const; |
| 103 | + |
| 104 | + // A map from expression ids to resolved references. |
| 105 | + // |
| 106 | + // The following entries are in this table: |
| 107 | + // |
| 108 | + // - An Ident or Select expression is represented here if it resolves to a |
| 109 | + // declaration. For instance, if `a.b.c` is represented by |
| 110 | + // `select(select(id(a), b), c)`, and `a.b` resolves to a declaration, |
| 111 | + // while `c` is a field selection, then the reference is attached to the |
| 112 | + // nested select expression (but not to the id or or the outer select). |
| 113 | + // In turn, if `a` resolves to a declaration and `b.c` are field selections, |
| 114 | + // the reference is attached to the ident expression. |
| 115 | + // - Every Call expression has an entry here, identifying the function being |
| 116 | + // called. |
| 117 | + // - Every CreateStruct expression for a message has an entry, identifying |
| 118 | + // the message. |
| 119 | + // |
| 120 | + // Unpopulated if the AST is not checked. |
| 121 | + const ReferenceMap& reference_map() const { return reference_map_; } |
| 122 | + ReferenceMap& mutable_reference_map() { return reference_map_; } |
| 123 | + |
| 124 | + // A map from expression ids to types. |
| 125 | + // |
| 126 | + // Every expression node which has a type different than DYN has a mapping |
| 127 | + // here. If an expression has type DYN, it is omitted from this map to save |
| 128 | + // space. |
| 129 | + // |
| 130 | + // Unpopulated if the AST is not checked. |
| 131 | + const TypeMap& type_map() const { return type_map_; } |
| 132 | + TypeMap& mutable_type_map() { return type_map_; } |
| 133 | + |
| 134 | + // The expr version indicates the major / minor version number of the `expr` |
| 135 | + // representation. |
| 136 | + // |
| 137 | + // The most common reason for a version change will be to indicate to the CEL |
| 138 | + // runtimes that transformations have been performed on the expr during static |
| 139 | + // analysis. |
| 140 | + absl::string_view expr_version() const { return expr_version_; } |
| 141 | + void set_expr_version(absl::string_view expr_version) { |
| 142 | + expr_version_ = expr_version; |
| 143 | + } |
44 | 144 |
|
45 | 145 | private: |
46 | | - // This interface should only be implemented by friend-visibility allowed |
47 | | - // subclasses. |
48 | | - Ast() = default; |
49 | 146 | friend class ast_internal::AstImpl; |
| 147 | + |
| 148 | + Expr root_expr_; |
| 149 | + SourceInfo source_info_; |
| 150 | + ReferenceMap reference_map_; |
| 151 | + TypeMap type_map_; |
| 152 | + std::string expr_version_; |
| 153 | + bool is_checked_; |
50 | 154 | }; |
51 | 155 |
|
52 | 156 | } // namespace cel |
|
0 commit comments