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