| // Copyright (c) 2013 The Chromium Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| #include "tools/gn/err.h" | |
| #include "tools/gn/functions.h" | |
| #include "tools/gn/parse_tree.h" | |
| #include "tools/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(); | |
| } | |
| // Extract the loop variable. | |
| const IdentifierNode* identifier = args_vector[0]->AsIdentifier(); | |
| if (!identifier) { | |
| *err = | |
| Err(args_vector[0].get(), "Expected an identifier for the loop var."); | |
| return Value(); | |
| } | |
| base::StringPiece loop_var(identifier->value().value()); | |
| // Extract the list to iterate over. Always copy in case the code changes | |
| // the list variable inside the loop. | |
| 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(); | |
| // Block to execute. | |
| const BlockNode* block = function->block(); | |
| if (!block) { | |
| *err = Err(function, "Expected { after foreach."); | |
| return Value(); | |
| } | |
| // If the loop variable was previously defined in this scope, save it so we | |
| // can put it back after the loop is done. | |
| 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(); | |
| } | |
| // Put back loop var. | |
| if (old_loop_value_ptr) { | |
| // Put back old value. Use the copy we made, rather than use the pointer, | |
| // which will probably point to the new value now in the scope. | |
| scope->SetValue(loop_var, std::move(old_loop_value), | |
| old_loop_value.origin()); | |
| } else { | |
| // Loop variable was undefined before loop, delete it. | |
| scope->RemoveIdentifier(loop_var); | |
| } | |
| return Value(); | |
| } | |
| } // namespace functions |