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