1// Copyright (c) 2013 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "gn/err.h" 6#include "gn/functions.h" 7#include "gn/parse_tree.h" 8#include "gn/scope.h" 9 10namespace functions { 11 12const char kForEach[] = "foreach"; 13const char kForEach_HelpShort[] = "foreach: Iterate over a list."; 14const char kForEach_Help[] = 15 R"(foreach: Iterate over a list. 16 17 foreach(<loop_var>, <list>) { 18 <loop contents> 19 } 20 21 Executes the loop contents block over each item in the list, assigning the 22 loop_var to each item in sequence. The <loop_var> will be a copy so assigning 23 to it will not mutate the list. The loop will iterate over a copy of <list> 24 so mutating it inside the loop will not affect iteration. 25 26 The block does not introduce a new scope, so that variable assignments inside 27 the loop will be visible once the loop terminates. 28 29 The loop variable will temporarily shadow any existing variables with the 30 same name for the duration of the loop. After the loop terminates the loop 31 variable will no longer be in scope, and the previous value (if any) will be 32 restored. 33 34Example 35 36 mylist = [ "a", "b", "c" ] 37 foreach(i, mylist) { 38 print(i) 39 } 40 41 Prints: 42 a 43 b 44 c 45)"; 46 47Value RunForEach(Scope* scope, 48 const FunctionCallNode* function, 49 const ListNode* args_list, 50 Err* err) { 51 const auto& args_vector = args_list->contents(); 52 if (args_vector.size() != 2) { 53 *err = Err(function, "Wrong number of arguments to foreach().", 54 "Expecting exactly two."); 55 return Value(); 56 } 57 58 // Extract the loop variable. 59 const IdentifierNode* identifier = args_vector[0]->AsIdentifier(); 60 if (!identifier) { 61 *err = 62 Err(args_vector[0].get(), "Expected an identifier for the loop var."); 63 return Value(); 64 } 65 std::string_view loop_var(identifier->value().value()); 66 67 // Extract the list to iterate over. Always copy in case the code changes 68 // the list variable inside the loop. 69 Value list_value = args_vector[1]->Execute(scope, err); 70 if (err->has_error()) 71 return Value(); 72 list_value.VerifyTypeIs(Value::Type::LIST, err); 73 if (err->has_error()) 74 return Value(); 75 const std::vector<Value>& list = list_value.list_value(); 76 77 // Block to execute. 78 const BlockNode* block = function->block(); 79 if (!block) { 80 *err = Err(function, "Expected { after foreach."); 81 return Value(); 82 } 83 84 // If the loop variable was previously defined in this scope, save it so we 85 // can put it back after the loop is done. 86 const Value* old_loop_value_ptr = scope->GetValue(loop_var); 87 Value old_loop_value; 88 if (old_loop_value_ptr) 89 old_loop_value = *old_loop_value_ptr; 90 91 for (const auto& cur : list) { 92 scope->SetValue(loop_var, cur, function); 93 block->Execute(scope, err); 94 if (err->has_error()) 95 return Value(); 96 } 97 98 // Put back loop var. 99 if (old_loop_value_ptr) { 100 // Put back old value. Use the copy we made, rather than use the pointer, 101 // which will probably point to the new value now in the scope. 102 scope->SetValue(loop_var, std::move(old_loop_value), 103 old_loop_value.origin()); 104 } else { 105 // Loop variable was undefined before loop, delete it. 106 scope->RemoveIdentifier(loop_var); 107 } 108 109 return Value(); 110} 111 112} // namespace functions 113