16d528ed9Sopenharmony_ci// Copyright (c) 2013 The Chromium Authors. All rights reserved. 26d528ed9Sopenharmony_ci// Use of this source code is governed by a BSD-style license that can be 36d528ed9Sopenharmony_ci// found in the LICENSE file. 46d528ed9Sopenharmony_ci 56d528ed9Sopenharmony_ci#include "gn/err.h" 66d528ed9Sopenharmony_ci#include "gn/functions.h" 76d528ed9Sopenharmony_ci#include "gn/parse_tree.h" 86d528ed9Sopenharmony_ci#include "gn/scope.h" 96d528ed9Sopenharmony_ci 106d528ed9Sopenharmony_cinamespace functions { 116d528ed9Sopenharmony_ci 126d528ed9Sopenharmony_ciconst char kForEach[] = "foreach"; 136d528ed9Sopenharmony_ciconst char kForEach_HelpShort[] = "foreach: Iterate over a list."; 146d528ed9Sopenharmony_ciconst char kForEach_Help[] = 156d528ed9Sopenharmony_ci R"(foreach: Iterate over a list. 166d528ed9Sopenharmony_ci 176d528ed9Sopenharmony_ci foreach(<loop_var>, <list>) { 186d528ed9Sopenharmony_ci <loop contents> 196d528ed9Sopenharmony_ci } 206d528ed9Sopenharmony_ci 216d528ed9Sopenharmony_ci Executes the loop contents block over each item in the list, assigning the 226d528ed9Sopenharmony_ci loop_var to each item in sequence. The <loop_var> will be a copy so assigning 236d528ed9Sopenharmony_ci to it will not mutate the list. The loop will iterate over a copy of <list> 246d528ed9Sopenharmony_ci so mutating it inside the loop will not affect iteration. 256d528ed9Sopenharmony_ci 266d528ed9Sopenharmony_ci The block does not introduce a new scope, so that variable assignments inside 276d528ed9Sopenharmony_ci the loop will be visible once the loop terminates. 286d528ed9Sopenharmony_ci 296d528ed9Sopenharmony_ci The loop variable will temporarily shadow any existing variables with the 306d528ed9Sopenharmony_ci same name for the duration of the loop. After the loop terminates the loop 316d528ed9Sopenharmony_ci variable will no longer be in scope, and the previous value (if any) will be 326d528ed9Sopenharmony_ci restored. 336d528ed9Sopenharmony_ci 346d528ed9Sopenharmony_ciExample 356d528ed9Sopenharmony_ci 366d528ed9Sopenharmony_ci mylist = [ "a", "b", "c" ] 376d528ed9Sopenharmony_ci foreach(i, mylist) { 386d528ed9Sopenharmony_ci print(i) 396d528ed9Sopenharmony_ci } 406d528ed9Sopenharmony_ci 416d528ed9Sopenharmony_ci Prints: 426d528ed9Sopenharmony_ci a 436d528ed9Sopenharmony_ci b 446d528ed9Sopenharmony_ci c 456d528ed9Sopenharmony_ci)"; 466d528ed9Sopenharmony_ci 476d528ed9Sopenharmony_ciValue RunForEach(Scope* scope, 486d528ed9Sopenharmony_ci const FunctionCallNode* function, 496d528ed9Sopenharmony_ci const ListNode* args_list, 506d528ed9Sopenharmony_ci Err* err) { 516d528ed9Sopenharmony_ci const auto& args_vector = args_list->contents(); 526d528ed9Sopenharmony_ci if (args_vector.size() != 2) { 536d528ed9Sopenharmony_ci *err = Err(function, "Wrong number of arguments to foreach().", 546d528ed9Sopenharmony_ci "Expecting exactly two."); 556d528ed9Sopenharmony_ci return Value(); 566d528ed9Sopenharmony_ci } 576d528ed9Sopenharmony_ci 586d528ed9Sopenharmony_ci // Extract the loop variable. 596d528ed9Sopenharmony_ci const IdentifierNode* identifier = args_vector[0]->AsIdentifier(); 606d528ed9Sopenharmony_ci if (!identifier) { 616d528ed9Sopenharmony_ci *err = 626d528ed9Sopenharmony_ci Err(args_vector[0].get(), "Expected an identifier for the loop var."); 636d528ed9Sopenharmony_ci return Value(); 646d528ed9Sopenharmony_ci } 656d528ed9Sopenharmony_ci std::string_view loop_var(identifier->value().value()); 666d528ed9Sopenharmony_ci 676d528ed9Sopenharmony_ci // Extract the list to iterate over. Always copy in case the code changes 686d528ed9Sopenharmony_ci // the list variable inside the loop. 696d528ed9Sopenharmony_ci Value list_value = args_vector[1]->Execute(scope, err); 706d528ed9Sopenharmony_ci if (err->has_error()) 716d528ed9Sopenharmony_ci return Value(); 726d528ed9Sopenharmony_ci list_value.VerifyTypeIs(Value::Type::LIST, err); 736d528ed9Sopenharmony_ci if (err->has_error()) 746d528ed9Sopenharmony_ci return Value(); 756d528ed9Sopenharmony_ci const std::vector<Value>& list = list_value.list_value(); 766d528ed9Sopenharmony_ci 776d528ed9Sopenharmony_ci // Block to execute. 786d528ed9Sopenharmony_ci const BlockNode* block = function->block(); 796d528ed9Sopenharmony_ci if (!block) { 806d528ed9Sopenharmony_ci *err = Err(function, "Expected { after foreach."); 816d528ed9Sopenharmony_ci return Value(); 826d528ed9Sopenharmony_ci } 836d528ed9Sopenharmony_ci 846d528ed9Sopenharmony_ci // If the loop variable was previously defined in this scope, save it so we 856d528ed9Sopenharmony_ci // can put it back after the loop is done. 866d528ed9Sopenharmony_ci const Value* old_loop_value_ptr = scope->GetValue(loop_var); 876d528ed9Sopenharmony_ci Value old_loop_value; 886d528ed9Sopenharmony_ci if (old_loop_value_ptr) 896d528ed9Sopenharmony_ci old_loop_value = *old_loop_value_ptr; 906d528ed9Sopenharmony_ci 916d528ed9Sopenharmony_ci for (const auto& cur : list) { 926d528ed9Sopenharmony_ci scope->SetValue(loop_var, cur, function); 936d528ed9Sopenharmony_ci block->Execute(scope, err); 946d528ed9Sopenharmony_ci if (err->has_error()) 956d528ed9Sopenharmony_ci return Value(); 966d528ed9Sopenharmony_ci } 976d528ed9Sopenharmony_ci 986d528ed9Sopenharmony_ci // Put back loop var. 996d528ed9Sopenharmony_ci if (old_loop_value_ptr) { 1006d528ed9Sopenharmony_ci // Put back old value. Use the copy we made, rather than use the pointer, 1016d528ed9Sopenharmony_ci // which will probably point to the new value now in the scope. 1026d528ed9Sopenharmony_ci scope->SetValue(loop_var, std::move(old_loop_value), 1036d528ed9Sopenharmony_ci old_loop_value.origin()); 1046d528ed9Sopenharmony_ci } else { 1056d528ed9Sopenharmony_ci // Loop variable was undefined before loop, delete it. 1066d528ed9Sopenharmony_ci scope->RemoveIdentifier(loop_var); 1076d528ed9Sopenharmony_ci } 1086d528ed9Sopenharmony_ci 1096d528ed9Sopenharmony_ci return Value(); 1106d528ed9Sopenharmony_ci} 1116d528ed9Sopenharmony_ci 1126d528ed9Sopenharmony_ci} // namespace functions 113