• 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/operators.h"
6 
7 #include <stddef.h>
8 #include <algorithm>
9 
10 #include "base/strings/string_number_conversions.h"
11 #include "gn/err.h"
12 #include "gn/parse_tree.h"
13 #include "gn/scope.h"
14 #include "gn/token.h"
15 #include "gn/value.h"
16 
17 namespace {
18 
19 // Helper class used for assignment operations: =, +=, and -= to generalize
20 // writing to various types of destinations.
21 class ValueDestination {
22  public:
23   ValueDestination();
24 
25   bool Init(Scope* exec_scope,
26             const ParseNode* dest,
27             const BinaryOpNode* op_node,
28             Err* err);
29 
30   // Returns the value in the destination scope if it already exists, or null
31   // if it doesn't. This is for validation and does not count as a "use".
32   // Other nested scopes will be searched.
33   const Value* GetExistingValue() const;
34 
35   // Returns an existing version of the output if it can be modified. This will
36   // not search nested scopes since writes only go into the current scope.
37   // Returns null if the value does not exist, or is not in the current scope
38   // (meaning assignments won't go to this value and it's not mutable). This
39   // is for implementing += and -=.
40   //
41   // If it exists, this will mark the origin of the value to be the passed-in
42   // node, and the value will be also marked unused (if possible) under the
43   // assumption that it will be modified in-place.
44   Value* GetExistingMutableValueIfExists(const ParseNode* origin);
45 
46   // Returns a pointer to the value that was set.
47   Value* SetValue(Value value, const ParseNode* set_node);
48 
49   // Fills the Err with an undefined value error appropriate for modification
50   // operators: += and -= (where the source is also the dest).
51   void MakeUndefinedIdentifierForModifyError(Err* err);
52 
53  private:
54   enum Type { UNINITIALIZED, SCOPE, LIST };
55 
56   Type type_;
57 
58   // Valid when type_ == SCOPE.
59   Scope* scope_;
60   const Token* name_token_;
61 
62   // Valid when type_ == LIST.
63   Value* list_;
64   size_t index_;  // Guaranteed in-range when Init() succeeds.
65 };
66 
ValueDestination()67 ValueDestination::ValueDestination()
68     : type_(UNINITIALIZED),
69       scope_(nullptr),
70       name_token_(nullptr),
71       list_(nullptr),
72       index_(0) {}
73 
Init(Scope * exec_scope,const ParseNode * dest,const BinaryOpNode * op_node,Err * err)74 bool ValueDestination::Init(Scope* exec_scope,
75                             const ParseNode* dest,
76                             const BinaryOpNode* op_node,
77                             Err* err) {
78   // Check for standard variable set.
79   const IdentifierNode* dest_identifier = dest->AsIdentifier();
80   if (dest_identifier) {
81     type_ = SCOPE;
82     scope_ = exec_scope;
83     name_token_ = &dest_identifier->value();
84     return true;
85   }
86 
87   // Check for array and scope accesses. The base (array or scope variable
88   // name) must always be defined ahead of time.
89   const AccessorNode* dest_accessor = dest->AsAccessor();
90   if (!dest_accessor) {
91     *err = Err(op_node, "Assignment requires a lvalue.",
92                "This thing on the left is not an identifier or accessor.");
93     err->AppendRange(dest->GetRange());
94     return false;
95   }
96 
97   // Known to be an accessor.
98   std::string_view base_str = dest_accessor->base().value();
99   Value* base =
100       exec_scope->GetMutableValue(base_str, Scope::SEARCH_CURRENT, false);
101   if (!base) {
102     // Base is either undefined or it's defined but not in the current scope.
103     // Make a good error message.
104     if (exec_scope->GetValue(base_str, false)) {
105       *err = Err(
106           dest_accessor->base(), "Suspicious in-place modification.",
107           "This variable exists in a containing scope. Normally, writing to it "
108           "would\nmake a copy of it into the current scope with the modified "
109           "version. But\nhere you're modifying only an element of a scope or "
110           "list object. It's unlikely\nyou meant to copy the entire thing just "
111           "to modify this part of it.\n"
112           "\n"
113           "If you really wanted to do this, do:\n"
114           "  " +
115               std::string(base_str) + " = " + std::string(base_str) +
116               "\n"
117               "to copy it into the current scope before doing this operation.");
118     } else {
119       *err = Err(dest_accessor->base(), "Undefined identifier.");
120     }
121     return false;
122   }
123 
124   if (dest_accessor->subscript()) {
125     // List access with an index.
126     if (!base->VerifyTypeIs(Value::LIST, err)) {
127       // Errors here will confusingly refer to the variable declaration (since
128       // that's all Value knows) rather than the list access. So rewrite the
129       // error location to refer to the base value's location.
130       *err = Err(dest_accessor->base(), err->message(), err->help_text());
131       return false;
132     }
133 
134     type_ = LIST;
135     list_ = base;
136     return dest_accessor->ComputeAndValidateListIndex(
137         exec_scope, base->list_value().size(), &index_, err);
138   }
139 
140   // Scope access with a dot.
141   if (!base->VerifyTypeIs(Value::SCOPE, err)) {
142     // As the for the list index case above, rewrite the error location.
143     *err = Err(dest_accessor->base(), err->message(), err->help_text());
144     return false;
145   }
146   type_ = SCOPE;
147   scope_ = base->scope_value();
148   name_token_ = &dest_accessor->member()->value();
149   return true;
150 }
151 
GetExistingValue() const152 const Value* ValueDestination::GetExistingValue() const {
153   if (type_ == SCOPE)
154     return scope_->GetValue(name_token_->value(), true);
155   else if (type_ == LIST)
156     return &list_->list_value()[index_];
157   return nullptr;
158 }
159 
GetExistingMutableValueIfExists(const ParseNode * origin)160 Value* ValueDestination::GetExistingMutableValueIfExists(
161     const ParseNode* origin) {
162   if (type_ == SCOPE) {
163     Value* value = scope_->GetMutableValue(name_token_->value(),
164                                            Scope::SEARCH_CURRENT, false);
165     if (value) {
166       // The value will be written to, reset its tracking information.
167       value->set_origin(origin);
168       scope_->MarkUnused(name_token_->value());
169     }
170   }
171   if (type_ == LIST)
172     return &list_->list_value()[index_];
173   return nullptr;
174 }
175 
SetValue(Value value,const ParseNode * set_node)176 Value* ValueDestination::SetValue(Value value, const ParseNode* set_node) {
177   if (type_ == SCOPE) {
178     return scope_->SetValue(name_token_->value(), std::move(value), set_node);
179   } else if (type_ == LIST) {
180     Value* dest = &list_->list_value()[index_];
181     *dest = std::move(value);
182     return dest;
183   }
184   return nullptr;
185 }
186 
MakeUndefinedIdentifierForModifyError(Err * err)187 void ValueDestination::MakeUndefinedIdentifierForModifyError(Err* err) {
188   // When Init() succeeds, the base of any accessor has already been resolved
189   // and that list indices are in-range. This means any undefined identifiers
190   // are for scope accesses.
191   DCHECK(type_ == SCOPE);
192   *err = Err(*name_token_, "Undefined identifier.");
193 }
194 
195 // Computes an error message for overwriting a nonempty list/scope with another.
MakeOverwriteError(const BinaryOpNode * op_node,const Value & old_value)196 Err MakeOverwriteError(const BinaryOpNode* op_node, const Value& old_value) {
197   std::string type_name;
198   std::string empty_def;
199 
200   if (old_value.type() == Value::LIST) {
201     type_name = "list";
202     empty_def = "[]";
203   } else if (old_value.type() == Value::SCOPE) {
204     type_name = "scope";
205     empty_def = "{}";
206   } else {
207     NOTREACHED();
208   }
209 
210   Err result(op_node->left()->GetRange(),
211              "Replacing nonempty " + type_name + ".",
212              "This overwrites a previously-defined nonempty " + type_name +
213                  " with another nonempty " + type_name + ".");
214   result.AppendSubErr(Err(
215       old_value, "for previous definition",
216       "Did you mean to append/modify instead? If you really want to overwrite, "
217       "do:\n"
218       "  foo = " +
219           empty_def + "\nbefore reassigning."));
220   return result;
221 }
222 
223 // -----------------------------------------------------------------------------
224 
MakeIncompatibleTypeError(const BinaryOpNode * op_node,const Value & left,const Value & right)225 Err MakeIncompatibleTypeError(const BinaryOpNode* op_node,
226                               const Value& left,
227                               const Value& right) {
228   std::string msg = std::string("You can't do <") +
229                     Value::DescribeType(left.type()) + "> " +
230                     std::string(op_node->op().value()) + " <" +
231                     Value::DescribeType(right.type()) + ">.";
232   if (left.type() == Value::LIST) {
233     // Append extra hint for list stuff.
234     msg +=
235         "\n\nHint: If you're attempting to add or remove a single item from "
236         " a list, use \"foo + [ bar ]\".";
237   }
238   return Err(op_node, "Incompatible types for binary operator.", msg);
239 }
240 
GetValueOrFillError(const BinaryOpNode * op_node,const ParseNode * node,const char * name,Scope * scope,Err * err)241 Value GetValueOrFillError(const BinaryOpNode* op_node,
242                           const ParseNode* node,
243                           const char* name,
244                           Scope* scope,
245                           Err* err) {
246   Value value = node->Execute(scope, err);
247   if (err->has_error())
248     return Value();
249   if (value.type() == Value::NONE) {
250     *err = Err(op_node->op(), "Operator requires a value.",
251                "This thing on the " + std::string(name) +
252                    " does not evaluate to a value.");
253     err->AppendRange(node->GetRange());
254     return Value();
255   }
256   return value;
257 }
258 
RemoveMatchesFromList(const BinaryOpNode * op_node,Value * list,const Value & to_remove,Err * err)259 void RemoveMatchesFromList(const BinaryOpNode* op_node,
260                            Value* list,
261                            const Value& to_remove,
262                            Err* err) {
263   std::vector<Value>& v = list->list_value();
264   switch (to_remove.type()) {
265     case Value::BOOLEAN:
266     case Value::INTEGER:  // Filter out the individual int/string.
267     case Value::STRING:
268     case Value::SCOPE: {
269       bool found_match = false;
270       for (size_t i = 0; i < v.size(); /* nothing */) {
271         if (v[i] == to_remove) {
272           found_match = true;
273           v.erase(v.begin() + i);
274         } else {
275           i++;
276         }
277       }
278       if (!found_match) {
279         *err = Err(to_remove.origin()->GetRange(), "Item not found",
280                    "You were trying to remove " + to_remove.ToString(true) +
281                        "\nfrom the list but it wasn't there.");
282       }
283       break;
284     }
285 
286     case Value::LIST:  // Filter out each individual thing.
287       for (const auto& elem : to_remove.list_value()) {
288         // TODO(brettw) if the nested item is a list, we may want to search
289         // for the literal list rather than remote the items in it.
290         RemoveMatchesFromList(op_node, list, elem, err);
291         if (err->has_error())
292           return;
293       }
294       break;
295 
296     case Value::NONE:
297       break;
298   }
299 }
300 
301 // Assignment -----------------------------------------------------------------
302 
303 // We return a null value from this rather than the result of doing the append.
304 // See ValuePlusEquals for rationale.
ExecuteEquals(Scope * exec_scope,const BinaryOpNode * op_node,ValueDestination * dest,Value right,Err * err)305 Value ExecuteEquals(Scope* exec_scope,
306                     const BinaryOpNode* op_node,
307                     ValueDestination* dest,
308                     Value right,
309                     Err* err) {
310   const Value* old_value = dest->GetExistingValue();
311   if (old_value) {
312     // Check for overwriting nonempty scopes or lists with other nonempty
313     // scopes or lists. This prevents mistakes that clobber a value rather than
314     // appending to it. For cases where a user meant to clear a value, allow
315     // overwriting a nonempty list/scope with an empty one, which can then be
316     // modified.
317     if (old_value->type() == Value::LIST && right.type() == Value::LIST &&
318         !old_value->list_value().empty() && !right.list_value().empty()) {
319       *err = MakeOverwriteError(op_node, *old_value);
320       return Value();
321     } else if (old_value->type() == Value::SCOPE &&
322                right.type() == Value::SCOPE &&
323                old_value->scope_value()->HasValues(Scope::SEARCH_CURRENT) &&
324                right.scope_value()->HasValues(Scope::SEARCH_CURRENT)) {
325       *err = MakeOverwriteError(op_node, *old_value);
326       return Value();
327     }
328   }
329 
330   dest->SetValue(std::move(right), op_node->right());
331   return Value();
332 }
333 
334 // Plus/minus ------------------------------------------------------------------
335 
336 // allow_left_type_conversion indicates if we're allowed to change the type of
337 // the left value. This is set to true when doing +, and false when doing +=.
ExecutePlus(const BinaryOpNode * op_node,Value left,Value right,bool allow_left_type_conversion,Err * err)338 Value ExecutePlus(const BinaryOpNode* op_node,
339                   Value left,
340                   Value right,
341                   bool allow_left_type_conversion,
342                   Err* err) {
343   // Left-hand-side integer.
344   if (left.type() == Value::INTEGER) {
345     if (right.type() == Value::INTEGER) {
346       // Int + int -> addition.
347       return Value(op_node, left.int_value() + right.int_value());
348     } else if (right.type() == Value::STRING && allow_left_type_conversion) {
349       // Int + string -> string concat.
350       return Value(op_node, base::Int64ToString(left.int_value()) +
351                                 right.string_value());
352     }
353     *err = MakeIncompatibleTypeError(op_node, left, right);
354     return Value();
355   }
356 
357   // Left-hand-side string.
358   if (left.type() == Value::STRING) {
359     if (right.type() == Value::INTEGER) {
360       // String + int -> string concat.
361       return Value(op_node, left.string_value() +
362                                 base::Int64ToString(right.int_value()));
363     } else if (right.type() == Value::STRING) {
364       // String + string -> string concat. Since the left is passed by copy
365       // we can avoid realloc if there is enough buffer by appending to left
366       // and assigning.
367       left.string_value().append(right.string_value());
368       return left;  // FIXME(brettw) des this copy?
369     }
370     *err = MakeIncompatibleTypeError(op_node, left, right);
371     return Value();
372   }
373 
374   // Left-hand-side list. The only valid thing is to add another list.
375   if (left.type() == Value::LIST && right.type() == Value::LIST) {
376     // Since left was passed by copy, avoid realloc by destructively appending
377     // to it and using that as the result.
378     for (Value& value : right.list_value())
379       left.list_value().push_back(std::move(value));
380     return left;  // FIXME(brettw) does this copy?
381   }
382 
383   *err = MakeIncompatibleTypeError(op_node, left, right);
384   return Value();
385 }
386 
387 // Left is passed by value because it will be modified in-place and returned
388 // for the list case.
ExecuteMinus(const BinaryOpNode * op_node,Value left,const Value & right,Err * err)389 Value ExecuteMinus(const BinaryOpNode* op_node,
390                    Value left,
391                    const Value& right,
392                    Err* err) {
393   // Left-hand-side int. The only thing to do is subtract another int.
394   if (left.type() == Value::INTEGER && right.type() == Value::INTEGER) {
395     // Int - int -> subtraction.
396     return Value(op_node, left.int_value() - right.int_value());
397   }
398 
399   // Left-hand-side list. The only thing to do is subtract another list.
400   if (left.type() == Value::LIST && right.type() == Value::LIST) {
401     // In-place modify left and return it.
402     RemoveMatchesFromList(op_node, &left, right, err);
403     return left;
404   }
405 
406   *err = MakeIncompatibleTypeError(op_node, left, right);
407   return Value();
408 }
409 
410 // In-place plus/minus ---------------------------------------------------------
411 
ExecutePlusEquals(Scope * exec_scope,const BinaryOpNode * op_node,ValueDestination * dest,Value right,Err * err)412 void ExecutePlusEquals(Scope* exec_scope,
413                        const BinaryOpNode* op_node,
414                        ValueDestination* dest,
415                        Value right,
416                        Err* err) {
417   // There are several cases. Some things we can convert "foo += bar" to
418   // "foo = foo + bar". Some cases we can't (the 'sources' variable won't
419   // get the right filtering on the list). Some cases we don't want to (lists
420   // and strings will get unnecessary copying so we can to optimize these).
421   //
422   //  - Value is already mutable in the current scope:
423   //     1. List/string append: use it.
424   //     2. Other types: fall back to "foo = foo + bar"
425   //
426   //  - Value is not mutable in the current scope:
427   //     3. List/string append: copy into current scope and append to that.
428   //     4. Other types: fall back to "foo = foo + bar"
429   //
430   // The common case is to use += for list and string appends in the local
431   // scope, so this is written to avoid multiple variable lookups in that case.
432   Value* mutable_dest = dest->GetExistingMutableValueIfExists(op_node);
433   if (!mutable_dest) {
434     const Value* existing_value = dest->GetExistingValue();
435     if (!existing_value) {
436       // Undefined left-hand-size for +=.
437       dest->MakeUndefinedIdentifierForModifyError(err);
438       return;
439     }
440 
441     if (existing_value->type() != Value::STRING &&
442         existing_value->type() != Value::LIST) {
443       // Case #4 above.
444       dest->SetValue(
445           ExecutePlus(op_node, *existing_value, std::move(right), false, err),
446           op_node);
447       return;
448     }
449 
450     // Case #3 above, copy to current scope and fall-through to appending.
451     mutable_dest = dest->SetValue(*existing_value, op_node);
452   } else if (mutable_dest->type() != Value::STRING &&
453              mutable_dest->type() != Value::LIST) {
454     // Case #2 above.
455     dest->SetValue(
456         ExecutePlus(op_node, *mutable_dest, std::move(right), false, err),
457         op_node);
458     return;
459   }  // "else" is case #1 above.
460 
461   if (mutable_dest->type() == Value::STRING) {
462     if (right.type() == Value::INTEGER) {
463       // String + int -> string concat.
464       mutable_dest->string_value().append(
465           base::Int64ToString(right.int_value()));
466     } else if (right.type() == Value::STRING) {
467       // String + string -> string concat.
468       mutable_dest->string_value().append(right.string_value());
469     } else {
470       *err = MakeIncompatibleTypeError(op_node, *mutable_dest, right);
471     }
472   } else if (mutable_dest->type() == Value::LIST) {
473     // List concat.
474     if (right.type() == Value::LIST) {
475       // Normal list concat. This is a destructive move.
476       for (Value& value : right.list_value())
477         mutable_dest->list_value().push_back(std::move(value));
478     } else {
479       *err = Err(op_node->op(), "Incompatible types to add.",
480                  "To append a single item to a list do \"foo += [ bar ]\".");
481     }
482   }
483 }
484 
ExecuteMinusEquals(const BinaryOpNode * op_node,ValueDestination * dest,const Value & right,Err * err)485 void ExecuteMinusEquals(const BinaryOpNode* op_node,
486                         ValueDestination* dest,
487                         const Value& right,
488                         Err* err) {
489   // Like the += case, we can convert "foo -= bar" to "foo = foo - bar". Since
490   // there is no sources filtering, this is always semantically valid. The
491   // only case we don't do it is for lists in the current scope which is the
492   // most common case, and also the one that can be optimized the most by
493   // doing it in-place.
494   Value* mutable_dest = dest->GetExistingMutableValueIfExists(op_node);
495   if (!mutable_dest ||
496       (mutable_dest->type() != Value::LIST || right.type() != Value::LIST)) {
497     const Value* existing_value = dest->GetExistingValue();
498     if (!existing_value) {
499       // Undefined left-hand-size for -=.
500       dest->MakeUndefinedIdentifierForModifyError(err);
501       return;
502     }
503     dest->SetValue(ExecuteMinus(op_node, *existing_value, right, err), op_node);
504     return;
505   }
506 
507   // In-place removal of items from "right".
508   RemoveMatchesFromList(op_node, mutable_dest, right, err);
509 }
510 
511 // Comparison -----------------------------------------------------------------
512 
ExecuteEqualsEquals(Scope * scope,const BinaryOpNode * op_node,const Value & left,const Value & right,Err * err)513 Value ExecuteEqualsEquals(Scope* scope,
514                           const BinaryOpNode* op_node,
515                           const Value& left,
516                           const Value& right,
517                           Err* err) {
518   if (left == right)
519     return Value(op_node, true);
520   return Value(op_node, false);
521 }
522 
ExecuteNotEquals(Scope * scope,const BinaryOpNode * op_node,const Value & left,const Value & right,Err * err)523 Value ExecuteNotEquals(Scope* scope,
524                        const BinaryOpNode* op_node,
525                        const Value& left,
526                        const Value& right,
527                        Err* err) {
528   // Evaluate in terms of ==.
529   Value result = ExecuteEqualsEquals(scope, op_node, left, right, err);
530   result.boolean_value() = !result.boolean_value();
531   return result;
532 }
533 
FillNeedsTwoIntegersError(const BinaryOpNode * op_node,const Value & left,const Value & right,Err * err)534 Value FillNeedsTwoIntegersError(const BinaryOpNode* op_node,
535                                 const Value& left,
536                                 const Value& right,
537                                 Err* err) {
538   *err = Err(op_node, "Comparison requires two integers.",
539              "This operator can only compare two integers.");
540   err->AppendRange(left.origin()->GetRange());
541   err->AppendRange(right.origin()->GetRange());
542   return Value();
543 }
544 
ExecuteLessEquals(Scope * scope,const BinaryOpNode * op_node,const Value & left,const Value & right,Err * err)545 Value ExecuteLessEquals(Scope* scope,
546                         const BinaryOpNode* op_node,
547                         const Value& left,
548                         const Value& right,
549                         Err* err) {
550   if (left.type() != Value::INTEGER || right.type() != Value::INTEGER)
551     return FillNeedsTwoIntegersError(op_node, left, right, err);
552   return Value(op_node, left.int_value() <= right.int_value());
553 }
554 
ExecuteGreaterEquals(Scope * scope,const BinaryOpNode * op_node,const Value & left,const Value & right,Err * err)555 Value ExecuteGreaterEquals(Scope* scope,
556                            const BinaryOpNode* op_node,
557                            const Value& left,
558                            const Value& right,
559                            Err* err) {
560   if (left.type() != Value::INTEGER || right.type() != Value::INTEGER)
561     return FillNeedsTwoIntegersError(op_node, left, right, err);
562   return Value(op_node, left.int_value() >= right.int_value());
563 }
564 
ExecuteGreater(Scope * scope,const BinaryOpNode * op_node,const Value & left,const Value & right,Err * err)565 Value ExecuteGreater(Scope* scope,
566                      const BinaryOpNode* op_node,
567                      const Value& left,
568                      const Value& right,
569                      Err* err) {
570   if (left.type() != Value::INTEGER || right.type() != Value::INTEGER)
571     return FillNeedsTwoIntegersError(op_node, left, right, err);
572   return Value(op_node, left.int_value() > right.int_value());
573 }
574 
ExecuteLess(Scope * scope,const BinaryOpNode * op_node,const Value & left,const Value & right,Err * err)575 Value ExecuteLess(Scope* scope,
576                   const BinaryOpNode* op_node,
577                   const Value& left,
578                   const Value& right,
579                   Err* err) {
580   if (left.type() != Value::INTEGER || right.type() != Value::INTEGER)
581     return FillNeedsTwoIntegersError(op_node, left, right, err);
582   return Value(op_node, left.int_value() < right.int_value());
583 }
584 
585 // Binary ----------------------------------------------------------------------
586 
ExecuteOr(Scope * scope,const BinaryOpNode * op_node,const ParseNode * left_node,const ParseNode * right_node,Err * err)587 Value ExecuteOr(Scope* scope,
588                 const BinaryOpNode* op_node,
589                 const ParseNode* left_node,
590                 const ParseNode* right_node,
591                 Err* err) {
592   Value left = GetValueOrFillError(op_node, left_node, "left", scope, err);
593   if (err->has_error())
594     return Value();
595   if (left.type() != Value::BOOLEAN) {
596     *err = Err(op_node->left(), "Left side of || operator is not a boolean.",
597                "Type is \"" + std::string(Value::DescribeType(left.type())) +
598                    "\" instead.");
599     return Value();
600   }
601   if (left.boolean_value())
602     return Value(op_node, left.boolean_value());
603 
604   Value right = GetValueOrFillError(op_node, right_node, "right", scope, err);
605   if (err->has_error())
606     return Value();
607   if (right.type() != Value::BOOLEAN) {
608     *err = Err(op_node->right(), "Right side of || operator is not a boolean.",
609                "Type is \"" + std::string(Value::DescribeType(right.type())) +
610                    "\" instead.");
611     return Value();
612   }
613 
614   return Value(op_node, left.boolean_value() || right.boolean_value());
615 }
616 
ExecuteAnd(Scope * scope,const BinaryOpNode * op_node,const ParseNode * left_node,const ParseNode * right_node,Err * err)617 Value ExecuteAnd(Scope* scope,
618                  const BinaryOpNode* op_node,
619                  const ParseNode* left_node,
620                  const ParseNode* right_node,
621                  Err* err) {
622   Value left = GetValueOrFillError(op_node, left_node, "left", scope, err);
623   if (err->has_error())
624     return Value();
625   if (left.type() != Value::BOOLEAN) {
626     *err = Err(op_node->left(), "Left side of && operator is not a boolean.",
627                "Type is \"" + std::string(Value::DescribeType(left.type())) +
628                    "\" instead.");
629     return Value();
630   }
631   if (!left.boolean_value())
632     return Value(op_node, left.boolean_value());
633 
634   Value right = GetValueOrFillError(op_node, right_node, "right", scope, err);
635   if (err->has_error())
636     return Value();
637   if (right.type() != Value::BOOLEAN) {
638     *err = Err(op_node->right(), "Right side of && operator is not a boolean.",
639                "Type is \"" + std::string(Value::DescribeType(right.type())) +
640                    "\" instead.");
641     return Value();
642   }
643   return Value(op_node, left.boolean_value() && right.boolean_value());
644 }
645 
646 }  // namespace
647 
648 // ----------------------------------------------------------------------------
649 
ExecuteUnaryOperator(Scope * scope,const UnaryOpNode * op_node,const Value & expr,Err * err)650 Value ExecuteUnaryOperator(Scope* scope,
651                            const UnaryOpNode* op_node,
652                            const Value& expr,
653                            Err* err) {
654   DCHECK(op_node->op().type() == Token::BANG);
655 
656   if (expr.type() != Value::BOOLEAN) {
657     *err = Err(op_node, "Operand of ! operator is not a boolean.",
658                "Type is \"" + std::string(Value::DescribeType(expr.type())) +
659                    "\" instead.");
660     return Value();
661   }
662   // TODO(scottmg): Why no unary minus?
663   return Value(op_node, !expr.boolean_value());
664 }
665 
ExecuteBinaryOperator(Scope * scope,const BinaryOpNode * op_node,const ParseNode * left,const ParseNode * right,Err * err)666 Value ExecuteBinaryOperator(Scope* scope,
667                             const BinaryOpNode* op_node,
668                             const ParseNode* left,
669                             const ParseNode* right,
670                             Err* err) {
671   const Token& op = op_node->op();
672 
673   // First handle the ones that take an lvalue.
674   if (op.type() == Token::EQUAL || op.type() == Token::PLUS_EQUALS ||
675       op.type() == Token::MINUS_EQUALS) {
676     // Compute the left side.
677     ValueDestination dest;
678     if (!dest.Init(scope, left, op_node, err))
679       return Value();
680 
681     // Compute the right side.
682     Value right_value = right->Execute(scope, err);
683     if (err->has_error())
684       return Value();
685     if (right_value.type() == Value::NONE) {
686       *err = Err(op, "Operator requires a rvalue.",
687                  "This thing on the right does not evaluate to a value.");
688       err->AppendRange(right->GetRange());
689       return Value();
690     }
691 
692     // "foo += bar" (same for "-=") is converted to "foo = foo + bar" here, but
693     // we pass the original value of "foo" by pointer to avoid a copy.
694     if (op.type() == Token::EQUAL) {
695       ExecuteEquals(scope, op_node, &dest, std::move(right_value), err);
696     } else if (op.type() == Token::PLUS_EQUALS) {
697       ExecutePlusEquals(scope, op_node, &dest, std::move(right_value), err);
698     } else if (op.type() == Token::MINUS_EQUALS) {
699       ExecuteMinusEquals(op_node, &dest, right_value, err);
700     } else {
701       NOTREACHED();
702     }
703     return Value();
704   }
705 
706   // ||, &&. Passed the node instead of the value so that they can avoid
707   // evaluating the RHS on early-out.
708   if (op.type() == Token::BOOLEAN_OR)
709     return ExecuteOr(scope, op_node, left, right, err);
710   if (op.type() == Token::BOOLEAN_AND)
711     return ExecuteAnd(scope, op_node, left, right, err);
712 
713   // Everything else works on the evaluated left and right values.
714   Value left_value = GetValueOrFillError(op_node, left, "left", scope, err);
715   if (err->has_error())
716     return Value();
717   Value right_value = GetValueOrFillError(op_node, right, "right", scope, err);
718   if (err->has_error())
719     return Value();
720 
721   // +, -.
722   if (op.type() == Token::MINUS)
723     return ExecuteMinus(op_node, std::move(left_value), right_value, err);
724   if (op.type() == Token::PLUS) {
725     return ExecutePlus(op_node, std::move(left_value), std::move(right_value),
726                        true, err);
727   }
728 
729   // Comparisons.
730   if (op.type() == Token::EQUAL_EQUAL)
731     return ExecuteEqualsEquals(scope, op_node, left_value, right_value, err);
732   if (op.type() == Token::NOT_EQUAL)
733     return ExecuteNotEquals(scope, op_node, left_value, right_value, err);
734   if (op.type() == Token::GREATER_EQUAL)
735     return ExecuteGreaterEquals(scope, op_node, left_value, right_value, err);
736   if (op.type() == Token::LESS_EQUAL)
737     return ExecuteLessEquals(scope, op_node, left_value, right_value, err);
738   if (op.type() == Token::GREATER_THAN)
739     return ExecuteGreater(scope, op_node, left_value, right_value, err);
740   if (op.type() == Token::LESS_THAN)
741     return ExecuteLess(scope, op_node, left_value, right_value, err);
742 
743   return Value();
744 }
745