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