Skip to content

Commit 860fcdd

Browse files
jnthntatumcopybara-github
authored andcommitted
Add native AST version of NavigableAst.
PiperOrigin-RevId: 800180843
1 parent 3209fd2 commit 860fcdd

4 files changed

Lines changed: 814 additions & 0 deletions

File tree

common/BUILD

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,40 @@ cc_test(
5858
],
5959
)
6060

61+
cc_library(
62+
name = "navigable_ast",
63+
srcs = ["navigable_ast.cc"],
64+
hdrs = ["navigable_ast.h"],
65+
deps = [
66+
":ast_traverse",
67+
":ast_visitor",
68+
":ast_visitor_base",
69+
":expr",
70+
"//common/ast:navigable_ast_internal",
71+
"@com_google_absl//absl/container:flat_hash_map",
72+
"@com_google_absl//absl/functional:any_invocable",
73+
"@com_google_absl//absl/memory",
74+
"@com_google_absl//absl/types:optional",
75+
],
76+
)
77+
78+
cc_test(
79+
name = "navigable_ast_test",
80+
srcs = ["navigable_ast_test.cc"],
81+
deps = [
82+
":ast",
83+
":expr",
84+
":navigable_ast",
85+
":source",
86+
":standard_definitions",
87+
"//internal:status_macros",
88+
"//internal:testing",
89+
"//parser",
90+
"@com_google_absl//absl/status:statusor",
91+
"@com_google_absl//absl/strings:string_view",
92+
],
93+
)
94+
6195
cc_library(
6296
name = "decl",
6397
srcs = ["decl.cc"],

common/navigable_ast.cc

Lines changed: 202 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,202 @@
1+
// Copyright 2025 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// https://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#include "common/navigable_ast.h"
16+
17+
#include <algorithm>
18+
#include <cstddef>
19+
#include <memory>
20+
#include <utility>
21+
#include <vector>
22+
23+
#include "absl/container/flat_hash_map.h"
24+
#include "absl/functional/any_invocable.h"
25+
#include "absl/memory/memory.h"
26+
#include "absl/types/optional.h"
27+
#include "common/ast/navigable_ast_internal.h"
28+
#include "common/ast_traverse.h"
29+
#include "common/ast_visitor.h"
30+
#include "common/ast_visitor_base.h"
31+
#include "common/expr.h"
32+
33+
namespace cel {
34+
35+
namespace {
36+
37+
using NavigableAstNodeData =
38+
common_internal::NavigableAstNodeData<common_internal::NativeAstTraits>;
39+
using NavigableAstMetadata =
40+
common_internal::NavigableAstMetadata<common_internal::NativeAstTraits>;
41+
42+
NodeKind GetNodeKind(const Expr& expr) {
43+
switch (expr.kind_case()) {
44+
case ExprKindCase::kConstant:
45+
return NodeKind::kConstant;
46+
case ExprKindCase::kIdentExpr:
47+
return NodeKind::kIdent;
48+
case ExprKindCase::kSelectExpr:
49+
return NodeKind::kSelect;
50+
case ExprKindCase::kCallExpr:
51+
return NodeKind::kCall;
52+
case ExprKindCase::kListExpr:
53+
return NodeKind::kList;
54+
case ExprKindCase::kStructExpr:
55+
return NodeKind::kStruct;
56+
case ExprKindCase::kMapExpr:
57+
return NodeKind::kMap;
58+
case ExprKindCase::kComprehensionExpr:
59+
return NodeKind::kComprehension;
60+
case ExprKindCase::kUnspecifiedExpr:
61+
default:
62+
return NodeKind::kUnspecified;
63+
}
64+
}
65+
66+
// Get the traversal relationship from parent to the given node.
67+
// Note: these depend on the ast_visitor utility's traversal ordering.
68+
ChildKind GetChildKind(const NavigableAstNodeData& parent_node,
69+
size_t child_index,
70+
absl::optional<ComprehensionArg> comprehension_arg) {
71+
switch (parent_node.node_kind) {
72+
case NodeKind::kStruct:
73+
return ChildKind::kStructValue;
74+
case NodeKind::kMap:
75+
if (child_index % 2 == 0) {
76+
return ChildKind::kMapKey;
77+
}
78+
return ChildKind::kMapValue;
79+
case NodeKind::kList:
80+
return ChildKind::kListElem;
81+
case NodeKind::kSelect:
82+
return ChildKind::kSelectOperand;
83+
case NodeKind::kCall:
84+
if (child_index == 0 && parent_node.expr->call_expr().has_target()) {
85+
return ChildKind::kCallReceiver;
86+
}
87+
return ChildKind::kCallArg;
88+
case NodeKind::kComprehension:
89+
if (!comprehension_arg.has_value()) {
90+
return ChildKind::kUnspecified;
91+
}
92+
switch (*comprehension_arg) {
93+
case ComprehensionArg::ITER_RANGE:
94+
return ChildKind::kComprehensionRange;
95+
case ComprehensionArg::ACCU_INIT:
96+
return ChildKind::kComprehensionInit;
97+
case ComprehensionArg::LOOP_CONDITION:
98+
return ChildKind::kComprehensionCondition;
99+
case ComprehensionArg::LOOP_STEP:
100+
return ChildKind::kComprehensionLoopStep;
101+
case ComprehensionArg::RESULT:
102+
return ChildKind::kComprensionResult;
103+
default:
104+
return ChildKind::kUnspecified;
105+
}
106+
default:
107+
return ChildKind::kUnspecified;
108+
}
109+
}
110+
111+
class NavigableExprBuilderVisitor : public cel::AstVisitorBase {
112+
public:
113+
NavigableExprBuilderVisitor(
114+
absl::AnyInvocable<std::unique_ptr<NavigableAstNode>()> node_factory,
115+
absl::AnyInvocable<NavigableAstNodeData&(NavigableAstNode&)>
116+
node_data_accessor)
117+
: node_factory_(std::move(node_factory)),
118+
node_data_accessor_(std::move(node_data_accessor)),
119+
metadata_(std::make_unique<NavigableAstMetadata>()) {}
120+
121+
NavigableAstNodeData& NodeDataAt(size_t index) {
122+
return node_data_accessor_(*metadata_->nodes[index]);
123+
}
124+
125+
void PreVisitExpr(const Expr& expr) override {
126+
NavigableAstNode* parent =
127+
parent_stack_.empty() ? nullptr
128+
: metadata_->nodes[parent_stack_.back()].get();
129+
size_t index = metadata_->nodes.size();
130+
metadata_->nodes.push_back(node_factory_());
131+
NavigableAstNode* node = metadata_->nodes[index].get();
132+
auto& node_data = NodeDataAt(index);
133+
node_data.parent = parent;
134+
node_data.expr = &expr;
135+
node_data.parent_relation = ChildKind::kUnspecified;
136+
node_data.node_kind = GetNodeKind(expr);
137+
node_data.tree_size = 1;
138+
node_data.height = 1;
139+
node_data.index = index;
140+
node_data.child_index = -1;
141+
node_data.metadata = metadata_.get();
142+
143+
metadata_->id_to_node.insert({expr.id(), node});
144+
metadata_->expr_to_node.insert({&expr, node});
145+
if (!parent_stack_.empty()) {
146+
auto& parent_node_data = NodeDataAt(parent_stack_.back());
147+
size_t child_index = parent_node_data.children.size();
148+
parent_node_data.children.push_back(node);
149+
node_data.parent_relation =
150+
GetChildKind(parent_node_data, child_index, comprehension_arg_);
151+
node_data.child_index = child_index;
152+
}
153+
parent_stack_.push_back(index);
154+
}
155+
156+
void PreVisitComprehensionSubexpression(
157+
const Expr& expr, const ComprehensionExpr& comprehension,
158+
ComprehensionArg comprehension_arg) override {
159+
comprehension_arg_ = comprehension_arg;
160+
}
161+
162+
void PostVisitExpr(const Expr& expr) override {
163+
size_t idx = parent_stack_.back();
164+
parent_stack_.pop_back();
165+
metadata_->postorder.push_back(metadata_->nodes[idx].get());
166+
NavigableAstNodeData& node = NodeDataAt(idx);
167+
if (!parent_stack_.empty()) {
168+
auto& parent_node_data = NodeDataAt(parent_stack_.back());
169+
parent_node_data.tree_size += node.tree_size;
170+
parent_node_data.height =
171+
std::max(parent_node_data.height, node.height + 1);
172+
}
173+
}
174+
175+
std::unique_ptr<NavigableAstMetadata> Consume() && {
176+
return std::move(metadata_);
177+
}
178+
179+
private:
180+
absl::AnyInvocable<std::unique_ptr<NavigableAstNode>()> node_factory_;
181+
absl::AnyInvocable<NavigableAstNodeData&(NavigableAstNode&)>
182+
node_data_accessor_;
183+
std::unique_ptr<NavigableAstMetadata> metadata_;
184+
std::vector<size_t> parent_stack_;
185+
absl::optional<ComprehensionArg> comprehension_arg_;
186+
};
187+
188+
} // namespace
189+
190+
NavigableAst NavigableAst::Build(const Expr& expr) {
191+
cel::TraversalOptions opts;
192+
opts.use_comprehension_callbacks = true;
193+
NavigableExprBuilderVisitor visitor(
194+
[]() { return absl::WrapUnique(new NavigableAstNode()); },
195+
[](NavigableAstNode& node) -> NavigableAstNodeData& {
196+
return node.data_;
197+
});
198+
AstTraverse(expr, visitor, opts);
199+
return NavigableAst(std::move(visitor).Consume());
200+
}
201+
202+
} // namespace cel

0 commit comments

Comments
 (0)