• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
10 namespace functions {
11 
12 const char kForEach[] = "foreach";
13 const char kForEach_HelpShort[] = "foreach: Iterate over a list.";
14 const 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 
34 Example
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 
RunForEach(Scope * scope,const FunctionCallNode * function,const ListNode * args_list,Err * err)47 Value 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