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