#include "gn/err.h"
#include "gn/functions.h"
#include "gn/parse_tree.h"
#include "gn/scope.h"
namespace functions {
const char kForEach[] = "foreach";
const char kForEach_HelpShort[] = "foreach: Iterate over a list.";
const char kForEach_Help[] =
R"(foreach: Iterate over a list.
foreach(<loop_var>, <list>) {
<loop contents>
}
Executes the loop contents block over each item in the list, assigning the
loop_var to each item in sequence. The <loop_var> will be a copy so assigning
to it will not mutate the list. The loop will iterate over a copy of <list>
so mutating it inside the loop will not affect iteration.
The block does not introduce a new scope, so that variable assignments inside
the loop will be visible once the loop terminates.
The loop variable will temporarily shadow any existing variables with the
same name for the duration of the loop. After the loop terminates the loop
variable will no longer be in scope, and the previous value (if any) will be
restored.
Example
mylist = [ "a", "b", "c" ]
foreach(i, mylist) {
print(i)
}
Prints:
a
b
c
)";
Value RunForEach(Scope* scope,
const FunctionCallNode* function,
const ListNode* args_list,
Err* err) {
const auto& args_vector = args_list->contents();
if (args_vector.size() != 2) {
*err = Err(function, "Wrong number of arguments to foreach().",
"Expecting exactly two.");
return Value();
}
const IdentifierNode* identifier = args_vector[0]->AsIdentifier();
if (!identifier) {
*err =
Err(args_vector[0].get(), "Expected an identifier for the loop var.");
return Value();
}
std::string_view loop_var(identifier->value().value());
Value list_value = args_vector[1]->Execute(scope, err);
if (err->has_error())
return Value();
list_value.VerifyTypeIs(Value::Type::LIST, err);
if (err->has_error())
return Value();
const std::vector<Value>& list = list_value.list_value();
const BlockNode* block = function->block();
if (!block) {
*err = Err(function, "Expected { after foreach.");
return Value();
}
const Value* old_loop_value_ptr = scope->GetValue(loop_var);
Value old_loop_value;
if (old_loop_value_ptr)
old_loop_value = *old_loop_value_ptr;
for (const auto& cur : list) {
scope->SetValue(loop_var, cur, function);
block->Execute(scope, err);
if (err->has_error())
return Value();
}
if (old_loop_value_ptr) {
scope->SetValue(loop_var, std::move(old_loop_value),
old_loop_value.origin());
} else {
scope->RemoveIdentifier(loop_var);
}
return Value();
}
}