Skip to content

Commit 31eb644

Browse files
committed
Implement range()
1 parent de532eb commit 31eb644

File tree

2 files changed

+61
-0
lines changed

2 files changed

+61
-0
lines changed

bant/frontend/elaboration.cc

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,9 @@ class SimpleElaborator : public BaseNodeReplacementVisitor {
102102
return maybe_macro;
103103
}
104104
}
105+
106+
// Implementing a few common functions. Getting out of hand to do this
107+
// inline here. Think of breaking them out.
105108
const std::string_view fun_name = f->identifier()->id();
106109
if (fun_name == "glob") {
107110
return HandleGlob(f);
@@ -112,6 +115,9 @@ class SimpleElaborator : public BaseNodeReplacementVisitor {
112115
if (fun_name == "len") {
113116
return HandleLen(f);
114117
}
118+
if (fun_name == "range") {
119+
return HandleRange(f);
120+
}
115121
if (options_.expand_load_functions && fun_name == "load") {
116122
HandleLoad(f);
117123
}
@@ -801,6 +807,39 @@ class SimpleElaborator : public BaseNodeReplacementVisitor {
801807
return fun;
802808
}
803809

810+
static std::optional<int64_t> GetIntAt(List *list, size_t pos) {
811+
if (pos >= list->size()) return std::nullopt;
812+
Scalar *const scalar = list->at(pos)->CastAsScalar();
813+
if (!scalar) return std::nullopt;
814+
if (scalar->type() != Scalar::ScalarType::kInt) return std::nullopt;
815+
return scalar->AsInt();
816+
}
817+
818+
Node *HandleRange(FunCall *fun) {
819+
if (fun->argument()->size() < 2) return fun;
820+
auto start = GetIntAt(fun->argument(), 0);
821+
auto end = GetIntAt(fun->argument(), 1);
822+
auto step = GetIntAt(fun->argument(), 2);
823+
if (!start || !end) return fun;
824+
if (!step.has_value()) {
825+
step = 1;
826+
}
827+
828+
List *result = Make<List>(List::Type::kList);
829+
if (step.value() == 0) {
830+
return result; // next best thing to an infinite range.
831+
}
832+
const int64_t range = *end - *start;
833+
int64_t elements = range / *step;
834+
// We don't co-routine yield that, just creating a concrete list.
835+
const auto &location = project_->GetLocation(fun->identifier()->id());
836+
if (elements <= 0 || elements > 20'000) return result; // prevent DoS
837+
for (int64_t i = *start; elements > 0; i += *step, elements--) {
838+
result->Append(project_->arena(), MakeIntWithStringRep(location, i));
839+
}
840+
return result;
841+
}
842+
804843
// Load potential starlark files and extract requested variables.
805844
void HandleLoad(FunCall *load_fun) {
806845
const auto args = query::ExtractStringList(load_fun->argument());

bant/frontend/elaboration_test.cc

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -545,6 +545,28 @@ EXAMPLE = "somefilename"
545545
EXPECT_EQ(result.first, result.second);
546546
}
547547

548+
TEST_F(ElaborationTest, RangeFunction) {
549+
auto result = ElabAndPrint(
550+
R"(
551+
FOO = [x for x in range(1, 1)]
552+
BAR = [x for x in range(-1, 1)]
553+
BAZ = [x for x in range(1, -1)]
554+
QUX = [x for x in range(7, 19, 3)]
555+
NUL = [x for x in range(7, 19, 0)]
556+
NEG = [x for x in range(19, 7, -2)]
557+
)",
558+
R"(
559+
FOO = []
560+
BAR = [-1, 0]
561+
BAZ = []
562+
QUX = [7, 10, 13, 16]
563+
NUL = []
564+
NEG = [19, 17, 15, 13, 11, 9]
565+
)");
566+
567+
EXPECT_EQ(result.first, result.second);
568+
}
569+
548570
TEST_F(ElaborationTest, Ternary) {
549571
auto result = ElabAndPrint(
550572
R"(

0 commit comments

Comments
 (0)