Skip to content

Commit cefb512

Browse files
jnthntatumcopybara-github
authored andcommitted
Add support for creating a subspan view of a cel::Source.
PiperOrigin-RevId: 934581306
1 parent 16074dc commit cefb512

7 files changed

Lines changed: 173 additions & 18 deletions

File tree

common/source.cc

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -585,6 +585,51 @@ absl::optional<std::pair<int32_t, SourcePosition>> Source::FindLine(
585585
return std::make_pair(line, line_offsets[static_cast<size_t>(line) - 2]);
586586
}
587587

588+
SourceSubrange::SourceSubrange(const Source& source, SourceRange range)
589+
: source_(source), range_(range) {
590+
SourcePosition size = source_.content().size();
591+
ABSL_DCHECK(range_.begin >= 0);
592+
ABSL_DCHECK(range_.begin <= size);
593+
ABSL_DCHECK(range_.end >= range_.begin);
594+
ABSL_DCHECK(range_.end <= size);
595+
if (range_.begin < 0) {
596+
range_.begin = 0;
597+
}
598+
if (range_.begin > size) {
599+
range_.begin = size;
600+
}
601+
if (range_.end < range_.begin) {
602+
range_.end = range_.begin;
603+
}
604+
if (range_.end > size) {
605+
range_.end = size;
606+
}
607+
for (const auto& line_offset : source_.line_offsets()) {
608+
if (line_offset > range_.begin && line_offset <= range_.end) {
609+
line_offsets_.push_back(line_offset - range_.begin);
610+
}
611+
}
612+
line_offsets_.push_back(range_.end - range_.begin + 1);
613+
}
614+
615+
SourceContentView SourceSubrange::content() const {
616+
auto parent_content = source_.content();
617+
if (parent_content.empty() || range_.begin >= range_.end) {
618+
return EmptyContentView();
619+
}
620+
return absl::visit(
621+
[this](auto view) {
622+
return SourceContentView(
623+
view.subspan(static_cast<size_t>(range_.begin),
624+
static_cast<size_t>(range_.end - range_.begin)));
625+
},
626+
parent_content.view_);
627+
}
628+
629+
absl::Span<const SourcePosition> SourceSubrange::line_offsets() const {
630+
return absl::MakeConstSpan(line_offsets_);
631+
}
632+
588633
absl::StatusOr<absl_nonnull SourcePtr> NewSource(absl::string_view content,
589634
std::string description) {
590635
return common_internal::NewSourceImpl(std::move(description), content,

common/source.h

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222

2323
#include "absl/base/attributes.h"
2424
#include "absl/base/nullability.h"
25+
#include "absl/container/inlined_vector.h"
2526
#include "absl/status/statusor.h"
2627
#include "absl/strings/cord.h"
2728
#include "absl/strings/string_view.h"
@@ -36,6 +37,7 @@ class SourceImpl;
3637
} // namespace common_internal
3738

3839
class Source;
40+
class SourceSubrange;
3941

4042
// SourcePosition represents an offset in source text.
4143
using SourcePosition = int32_t;
@@ -94,6 +96,7 @@ class SourceContentView final {
9496

9597
private:
9698
friend class Source;
99+
friend class SourceSubrange;
97100

98101
constexpr SourceContentView() = default;
99102

@@ -178,6 +181,7 @@ class Source {
178181

179182
private:
180183
friend class common_internal::SourceImpl;
184+
friend class SourceSubrange;
181185

182186
Source() = default;
183187

@@ -187,6 +191,34 @@ class Source {
187191
SourcePosition position) const;
188192
};
189193

194+
// `SourceSubrange` is a view of a subrange fo an underlying `Source` object.
195+
// Intended to be used when the CEL expression is embedded in a larger text
196+
// representation (such as a.celpolicy).
197+
//
198+
// The parent `Source` must outlive this object.
199+
class SourceSubrange final : public Source {
200+
public:
201+
SourceSubrange(const Source& source ABSL_ATTRIBUTE_LIFETIME_BOUND,
202+
SourceRange range);
203+
204+
absl::string_view description() const ABSL_ATTRIBUTE_LIFETIME_BOUND override {
205+
return source_.description();
206+
}
207+
208+
// Returns a view of the underlying expression text.
209+
ContentView content() const ABSL_ATTRIBUTE_LIFETIME_BOUND override;
210+
211+
// Returns a `absl::Span` of `SourcePosition` which represent the positions
212+
// where new lines occur.
213+
absl::Span<const SourcePosition> line_offsets() const
214+
ABSL_ATTRIBUTE_LIFETIME_BOUND override;
215+
216+
private:
217+
const Source& source_;
218+
SourceRange range_;
219+
absl::InlinedVector<SourcePosition, 1> line_offsets_;
220+
};
221+
190222
using SourcePtr = std::unique_ptr<Source>;
191223

192224
absl::StatusOr<absl_nonnull SourcePtr> NewSource(

common/source_test.cc

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,5 +223,44 @@ TEST(Source, DisplayErrorLocationFullWidth) {
223223
"\n | ..^");
224224
}
225225

226+
TEST(SourceSubrange, Description) {
227+
ASSERT_OK_AND_ASSIGN(auto source, NewSource("hello world", "subrange-test"));
228+
SourceSubrange subrange(*source, SourceRange{0, 5});
229+
EXPECT_THAT(subrange.description(), Eq("subrange-test"));
230+
}
231+
232+
TEST(SourceSubrange, Content) {
233+
ASSERT_OK_AND_ASSIGN(auto source, NewSource("hello world", "subrange-test"));
234+
SourceSubrange subrange(*source, SourceRange{6, 11});
235+
EXPECT_THAT(subrange.content().ToString(), Eq("world"));
236+
}
237+
238+
TEST(SourceSubrange, ContentEmpty) {
239+
ASSERT_OK_AND_ASSIGN(auto source, NewSource("hello world", "subrange-test"));
240+
SourceSubrange subrange(*source, SourceRange{5, 5});
241+
EXPECT_THAT(subrange.content().ToString(), Eq(""));
242+
}
243+
244+
TEST(SourceSubrange, LineOffsetsNoNewlines) {
245+
ASSERT_OK_AND_ASSIGN(auto source,
246+
NewSource("hello\nworld\n", "subrange-test"));
247+
SourceSubrange subrange(*source, SourceRange{0, 5});
248+
EXPECT_THAT(subrange.line_offsets(), ElementsAre(6));
249+
}
250+
251+
TEST(SourceSubrange, LineOffsetsWithNewlines) {
252+
ASSERT_OK_AND_ASSIGN(auto source,
253+
NewSource("hello\nworld\ncel", "subrange-test"));
254+
SourceSubrange subrange(*source, SourceRange{0, 11});
255+
EXPECT_THAT(subrange.line_offsets(), ElementsAre(6, 12));
256+
}
257+
258+
TEST(SourceSubrange, LineOffsetsMiddleSubrange) {
259+
ASSERT_OK_AND_ASSIGN(auto source,
260+
NewSource("hello\nworld\ncel\ncpp", "subrange-test"));
261+
SourceSubrange subrange(*source, SourceRange{6, 15});
262+
EXPECT_THAT(subrange.line_offsets(), ElementsAre(6, 10));
263+
}
264+
226265
} // namespace
227266
} // namespace cel

compiler/BUILD

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ cc_library(
2525
"//checker:type_checker",
2626
"//checker:type_checker_builder",
2727
"//checker:validation_result",
28+
"//common:source",
2829
"//parser:options",
2930
"//parser:parser_interface",
3031
"//validator",

compiler/compiler.h

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
#include "checker/type_checker.h"
2828
#include "checker/type_checker_builder.h"
2929
#include "checker/validation_result.h"
30+
#include "common/source.h"
3031
#include "parser/options.h"
3132
#include "parser/parser_interface.h"
3233
#include "validator/validator.h"
@@ -129,9 +130,18 @@ class Compiler {
129130
public:
130131
virtual ~Compiler() = default;
131132

132-
virtual absl::StatusOr<ValidationResult> Compile(
133+
absl::StatusOr<ValidationResult> Compile(
134+
const Source& source, google::protobuf::Arena* absl_nullable arena) const {
135+
return CompileImpl(source, arena);
136+
}
137+
138+
absl::StatusOr<ValidationResult> Compile(const Source& source) const {
139+
return CompileImpl(source, nullptr);
140+
}
141+
142+
absl::StatusOr<ValidationResult> Compile(
133143
absl::string_view source, absl::string_view description,
134-
google::protobuf::Arena* absl_nullable arena) const = 0;
144+
google::protobuf::Arena* absl_nullable arena) const;
135145

136146
absl::StatusOr<ValidationResult> Compile(absl::string_view source) const {
137147
return Compile(source, "<input>", nullptr);
@@ -159,8 +169,27 @@ class Compiler {
159169
// The returned builder does not share state with the compiler and may be
160170
// modified independently.
161171
virtual std::unique_ptr<CompilerBuilder> ToBuilder() const = 0;
172+
173+
protected:
174+
virtual absl::StatusOr<ValidationResult> CompileImpl(
175+
const Source& source, google::protobuf::Arena* absl_nullable arena) const = 0;
162176
};
163177

178+
inline absl::StatusOr<ValidationResult> Compiler::Compile(
179+
absl::string_view source, absl::string_view description,
180+
google::protobuf::Arena* absl_nullable arena) const {
181+
absl::StatusOr<SourcePtr> source_obj =
182+
NewSource(source, std::string(description));
183+
if (!source_obj.ok()) {
184+
return source_obj.status();
185+
}
186+
absl::StatusOr<ValidationResult> result = CompileImpl(**source_obj, arena);
187+
if (result.ok()) {
188+
result->SetSource(std::move(*source_obj));
189+
}
190+
return result;
191+
}
192+
164193
} // namespace cel
165194

166195
#endif // THIRD_PARTY_CEL_CPP_COMPILER_COMPILER_INTERFACE_H_

compiler/compiler_factory.cc

Lines changed: 11 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -54,14 +54,18 @@ class CompilerImpl : public Compiler {
5454
validator_(std::move(validator)),
5555
options_(options) {}
5656

57-
absl::StatusOr<ValidationResult> Compile(
58-
absl::string_view expression, absl::string_view description,
59-
google::protobuf::Arena* arena) const override {
60-
CEL_ASSIGN_OR_RETURN(auto source,
61-
cel::NewSource(expression, std::string(description)));
57+
std::unique_ptr<CompilerBuilder> ToBuilder() const override;
58+
59+
const TypeChecker& GetTypeChecker() const override { return *type_checker_; }
60+
const Parser& GetParser() const override { return *parser_; }
61+
const Validator& GetValidator() const override { return validator_; }
62+
63+
protected:
64+
absl::StatusOr<ValidationResult> CompileImpl(
65+
const Source& source, google::protobuf::Arena* arena) const override {
6266
std::vector<cel::ParseIssue> parse_issues;
6367
absl::StatusOr<std::unique_ptr<cel::Ast>> ast =
64-
parser_->Parse(*source, &parse_issues);
68+
parser_->Parse(source, &parse_issues);
6569
if (!ast.ok()) {
6670
if (!options_.adapt_parser_errors ||
6771
ast.status().code() != absl::StatusCode::kInvalidArgument ||
@@ -74,26 +78,17 @@ class CompilerImpl : public Compiler {
7478
check_issues.push_back(TypeCheckIssue::CreateError(
7579
issue.location(), std::string(issue.message())));
7680
}
77-
ValidationResult result(std::move(check_issues));
78-
result.SetSource(std::move(source));
79-
return result;
81+
return ValidationResult(std::move(check_issues));
8082
}
8183
CEL_ASSIGN_OR_RETURN(ValidationResult result,
8284
type_checker_->Check(*std::move(ast), arena));
8385

84-
result.SetSource(std::move(source));
8586
if (!validator_.validations().empty()) {
8687
validator_.UpdateValidationResult(result);
8788
}
8889
return result;
8990
}
9091

91-
std::unique_ptr<CompilerBuilder> ToBuilder() const override;
92-
93-
const TypeChecker& GetTypeChecker() const override { return *type_checker_; }
94-
const Parser& GetParser() const override { return *parser_; }
95-
const Validator& GetValidator() const override { return validator_; }
96-
9792
private:
9893
std::unique_ptr<TypeChecker> type_checker_;
9994
std::unique_ptr<Parser> parser_;

compiler/compiler_factory_test.cc

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -427,5 +427,19 @@ TEST(CompilerFactoryTest, ReturnsIssuesFromParser) {
427427
EXPECT_THAT(result.GetIssues(), testing::Not(testing::IsEmpty()));
428428
}
429429

430+
TEST(CompilerFactoryTest, CompileSourceOverload) {
431+
ASSERT_OK_AND_ASSIGN(
432+
auto builder,
433+
NewCompilerBuilder(cel::internal::GetSharedTestingDescriptorPool()));
434+
435+
ASSERT_THAT(builder->AddLibrary(StandardCompilerLibrary()), IsOk());
436+
ASSERT_OK_AND_ASSIGN(auto compiler, builder->Build());
437+
438+
ASSERT_OK_AND_ASSIGN(auto source, cel::NewSource("1 + 2"));
439+
ASSERT_OK_AND_ASSIGN(ValidationResult result, compiler->Compile(*source));
440+
441+
EXPECT_TRUE(result.IsValid());
442+
}
443+
430444
} // namespace
431445
} // namespace cel

0 commit comments

Comments
 (0)