Revert 264226 "Reduce dependency of TiclInvalidationService on P..."
[chromium-blink-merge.git] / tools / gn / operators.cc
blobd22b7d15bb04fddaa78f22abef80fdc9bb294c74
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.
5 #include "tools/gn/operators.h"
7 #include "base/strings/string_number_conversions.h"
8 #include "tools/gn/err.h"
9 #include "tools/gn/parse_tree.h"
10 #include "tools/gn/scope.h"
11 #include "tools/gn/token.h"
12 #include "tools/gn/value.h"
14 namespace {
16 const char kSourcesName[] = "sources";
18 // Applies the sources assignment filter from the given scope to each element
19 // of source (can be a list or a string), appending it to dest if it doesn't
20 // match.
21 void AppendFilteredSourcesToValue(const Scope* scope,
22 const Value& source,
23 Value* dest) {
24 const PatternList* filter = scope->GetSourcesAssignmentFilter();
26 if (source.type() == Value::STRING) {
27 if (!filter || filter->is_empty() ||
28 !filter->MatchesValue(source))
29 dest->list_value().push_back(source);
30 return;
32 if (source.type() != Value::LIST) {
33 // Any non-list and non-string being added to a list can just get appended,
34 // we're not going to filter it.
35 dest->list_value().push_back(source);
36 return;
39 const std::vector<Value>& source_list = source.list_value();
40 if (!filter || filter->is_empty()) {
41 // No filter, append everything.
42 for (size_t i = 0; i < source_list.size(); i++)
43 dest->list_value().push_back(source_list[i]);
44 return;
47 // Note: don't reserve() the dest vector here since that actually hurts
48 // the allocation pattern when the build script is doing multiple small
49 // additions.
50 for (size_t i = 0; i < source_list.size(); i++) {
51 if (!filter->MatchesValue(source_list[i]))
52 dest->list_value().push_back(source_list[i]);
56 void RemoveMatchesFromList(const BinaryOpNode* op_node,
57 Value* list,
58 const Value& to_remove,
59 Err* err) {
60 std::vector<Value>& v = list->list_value();
61 switch (to_remove.type()) {
62 case Value::BOOLEAN:
63 case Value::INTEGER: // Filter out the individual int/string.
64 case Value::STRING: {
65 bool found_match = false;
66 for (size_t i = 0; i < v.size(); /* nothing */) {
67 if (v[i] == to_remove) {
68 found_match = true;
69 v.erase(v.begin() + i);
70 } else {
71 i++;
74 if (!found_match) {
75 *err = Err(to_remove.origin()->GetRange(), "Item not found",
76 "You were trying to remove " + to_remove.ToString(true) +
77 "\nfrom the list but it wasn't there.");
79 break;
82 case Value::LIST: // Filter out each individual thing.
83 for (size_t i = 0; i < to_remove.list_value().size(); i++) {
84 // TODO(brettw) if the nested item is a list, we may want to search
85 // for the literal list rather than remote the items in it.
86 RemoveMatchesFromList(op_node, list, to_remove.list_value()[i], err);
87 if (err->has_error())
88 return;
90 break;
92 default:
93 break;
97 // Assignment -----------------------------------------------------------------
99 // We return a null value from this rather than the result of doing the append.
100 // See ValuePlusEquals for rationale.
101 Value ExecuteEquals(Scope* scope,
102 const BinaryOpNode* op_node,
103 const Token& left,
104 const Value& right,
105 Err* err) {
106 const Value* old_value = scope->GetValue(left.value(), false);
107 if (old_value) {
108 // Throw an error when overwriting a nonempty list with another nonempty
109 // list item. This is to detect the case where you write
110 // defines = ["FOO"]
111 // and you overwrote inherited ones, when instead you mean to append:
112 // defines += ["FOO"]
113 if (old_value->type() == Value::LIST &&
114 !old_value->list_value().empty() &&
115 right.type() == Value::LIST &&
116 !right.list_value().empty()) {
117 *err = Err(op_node->left()->GetRange(), "Replacing nonempty list.",
118 std::string("This overwrites a previously-defined nonempty list ") +
119 "(length " +
120 base::IntToString(static_cast<int>(old_value->list_value().size()))
121 + ").");
122 err->AppendSubErr(Err(*old_value, "for previous definition",
123 "with another one (length " +
124 base::IntToString(static_cast<int>(right.list_value().size())) +
125 "). Did you mean " +
126 "\"+=\" to append instead? If you\nreally want to do this, do\n " +
127 left.value().as_string() + " = []\nbefore reassigning."));
128 return Value();
131 if (err->has_error())
132 return Value();
134 if (right.type() == Value::LIST && left.value() == kSourcesName) {
135 // Assigning to sources, filter the list. Here we do the filtering and
136 // copying in one step to save an extra list copy (the lists may be
137 // long).
138 Value* set_value = scope->SetValue(left.value(),
139 Value(op_node, Value::LIST), op_node);
140 set_value->list_value().reserve(right.list_value().size());
141 AppendFilteredSourcesToValue(scope, right, set_value);
142 } else {
143 // Normal value set, just copy it.
144 scope->SetValue(left.value(), right, op_node->right());
146 return Value();
149 // allow_type_conversion indicates if we're allowed to change the type of the
150 // left value. This is set to true when doing +, and false when doing +=.
152 // Note that we return Value() from here, which is different than C++. This
153 // means you can't do clever things like foo = [ bar += baz ] to simultaneously
154 // append to and use a value. This is basically never needed in out build
155 // scripts and is just as likely an error as the intended behavior, and it also
156 // involves a copy of the value when it's returned. Many times we're appending
157 // to large lists, and copying the value to discard it for the next statement
158 // is very wasteful.
159 void ValuePlusEquals(const Scope* scope,
160 const BinaryOpNode* op_node,
161 const Token& left_token,
162 Value* left,
163 const Value& right,
164 bool allow_type_conversion,
165 Err* err) {
166 switch (left->type()) {
167 // Left-hand-side int.
168 case Value::INTEGER:
169 switch (right.type()) {
170 case Value::INTEGER: // int + int -> addition.
171 left->int_value() += right.int_value();
172 return;
174 case Value::STRING: // int + string -> string concat.
175 if (allow_type_conversion) {
176 *left = Value(op_node,
177 base::Int64ToString(left->int_value()) + right.string_value());
178 return;
180 break;
182 default:
183 break;
185 break;
187 // Left-hand-side string.
188 case Value::STRING:
189 switch (right.type()) {
190 case Value::INTEGER: // string + int -> string concat.
191 left->string_value().append(base::Int64ToString(right.int_value()));
192 return;
194 case Value::STRING: // string + string -> string contat.
195 left->string_value().append(right.string_value());
196 return;
198 default:
199 break;
201 break;
203 // Left-hand-side list.
204 case Value::LIST:
205 switch (right.type()) {
206 case Value::LIST: // list + list -> list concat.
207 if (left_token.value() == kSourcesName) {
208 // Filter additions through the assignment filter.
209 AppendFilteredSourcesToValue(scope, right, left);
210 } else {
211 // Normal list concat.
212 for (size_t i = 0; i < right.list_value().size(); i++)
213 left->list_value().push_back(right.list_value()[i]);
215 return;
217 default:
218 *err = Err(op_node->op(), "Incompatible types to add.",
219 "To append a single item to a list do \"foo += [ bar ]\".");
220 return;
223 default:
224 break;
227 *err = Err(op_node->op(), "Incompatible types to add.",
228 std::string("I see a ") + Value::DescribeType(left->type()) + " and a " +
229 Value::DescribeType(right.type()) + ".");
232 Value ExecutePlusEquals(Scope* scope,
233 const BinaryOpNode* op_node,
234 const Token& left,
235 const Value& right,
236 Err* err) {
237 // We modify in-place rather than doing read-modify-write to avoid
238 // copying large lists.
239 Value* left_value =
240 scope->GetValueForcedToCurrentScope(left.value(), op_node);
241 if (!left_value) {
242 *err = Err(left, "Undefined variable for +=.",
243 "I don't have something with this name in scope now.");
244 return Value();
246 ValuePlusEquals(scope, op_node, left, left_value, right, false, err);
247 left_value->set_origin(op_node);
248 scope->MarkUnused(left.value());
249 return Value();
252 // We return a null value from this rather than the result of doing the append.
253 // See ValuePlusEquals for rationale.
254 void ValueMinusEquals(const BinaryOpNode* op_node,
255 Value* left,
256 const Value& right,
257 bool allow_type_conversion,
258 Err* err) {
259 switch (left->type()) {
260 // Left-hand-side int.
261 case Value::INTEGER:
262 switch (right.type()) {
263 case Value::INTEGER: // int - int -> subtraction.
264 left->int_value() -= right.int_value();
265 return;
267 default:
268 break;
270 break;
272 // Left-hand-side string.
273 case Value::STRING:
274 break; // All are errors.
276 // Left-hand-side list.
277 case Value::LIST:
278 if (right.type() != Value::LIST) {
279 *err = Err(op_node->op(), "Incompatible types to subtract.",
280 "To remove a single item from a list do \"foo -= [ bar ]\".");
281 } else {
282 RemoveMatchesFromList(op_node, left, right, err);
284 return;
286 default:
287 break;
290 *err = Err(op_node->op(), "Incompatible types to subtract.",
291 std::string("I see a ") + Value::DescribeType(left->type()) + " and a " +
292 Value::DescribeType(right.type()) + ".");
295 Value ExecuteMinusEquals(Scope* scope,
296 const BinaryOpNode* op_node,
297 const Token& left,
298 const Value& right,
299 Err* err) {
300 Value* left_value =
301 scope->GetValueForcedToCurrentScope(left.value(), op_node);
302 if (!left_value) {
303 *err = Err(left, "Undefined variable for -=.",
304 "I don't have something with this name in scope now.");
305 return Value();
307 ValueMinusEquals(op_node, left_value, right, false, err);
308 left_value->set_origin(op_node);
309 scope->MarkUnused(left.value());
310 return Value();
313 // Plus/Minus -----------------------------------------------------------------
315 Value ExecutePlus(Scope* scope,
316 const BinaryOpNode* op_node,
317 const Value& left,
318 const Value& right,
319 Err* err) {
320 Value ret = left;
321 ValuePlusEquals(scope, op_node, Token(), &ret, right, true, err);
322 ret.set_origin(op_node);
323 return ret;
326 Value ExecuteMinus(Scope* scope,
327 const BinaryOpNode* op_node,
328 const Value& left,
329 const Value& right,
330 Err* err) {
331 Value ret = left;
332 ValueMinusEquals(op_node, &ret, right, true, err);
333 ret.set_origin(op_node);
334 return ret;
337 // Comparison -----------------------------------------------------------------
339 Value ExecuteEqualsEquals(Scope* scope,
340 const BinaryOpNode* op_node,
341 const Value& left,
342 const Value& right,
343 Err* err) {
344 if (left == right)
345 return Value(op_node, true);
346 return Value(op_node, false);
349 Value ExecuteNotEquals(Scope* scope,
350 const BinaryOpNode* op_node,
351 const Value& left,
352 const Value& right,
353 Err* err) {
354 // Evaluate in terms of ==.
355 Value result = ExecuteEqualsEquals(scope, op_node, left, right, err);
356 result.boolean_value() = !result.boolean_value();
357 return result;
360 Value FillNeedsTwoIntegersError(const BinaryOpNode* op_node,
361 const Value& left,
362 const Value& right,
363 Err* err) {
364 *err = Err(op_node, "Comparison requires two integers.",
365 "This operator can only compare two integers.");
366 err->AppendRange(left.origin()->GetRange());
367 err->AppendRange(right.origin()->GetRange());
368 return Value();
371 Value ExecuteLessEquals(Scope* scope,
372 const BinaryOpNode* op_node,
373 const Value& left,
374 const Value& right,
375 Err* err) {
376 if (left.type() != Value::INTEGER || right.type() != Value::INTEGER)
377 return FillNeedsTwoIntegersError(op_node, left, right, err);
378 return Value(op_node, left.int_value() <= right.int_value());
381 Value ExecuteGreaterEquals(Scope* scope,
382 const BinaryOpNode* op_node,
383 const Value& left,
384 const Value& right,
385 Err* err) {
386 if (left.type() != Value::INTEGER || right.type() != Value::INTEGER)
387 return FillNeedsTwoIntegersError(op_node, left, right, err);
388 return Value(op_node, left.int_value() >= right.int_value());
391 Value ExecuteGreater(Scope* scope,
392 const BinaryOpNode* op_node,
393 const Value& left,
394 const Value& right,
395 Err* err) {
396 if (left.type() != Value::INTEGER || right.type() != Value::INTEGER)
397 return FillNeedsTwoIntegersError(op_node, left, right, err);
398 return Value(op_node, left.int_value() > right.int_value());
401 Value ExecuteLess(Scope* scope,
402 const BinaryOpNode* op_node,
403 const Value& left,
404 const Value& right,
405 Err* err) {
406 if (left.type() != Value::INTEGER || right.type() != Value::INTEGER)
407 return FillNeedsTwoIntegersError(op_node, left, right, err);
408 return Value(op_node, left.int_value() < right.int_value());
411 // Binary ----------------------------------------------------------------------
413 Value ExecuteOr(Scope* scope,
414 const BinaryOpNode* op_node,
415 const Value& left,
416 const Value& right,
417 Err* err) {
418 if (left.type() != Value::BOOLEAN) {
419 *err = Err(op_node->left(), "Left side of || operator is not a boolean.",
420 "Type is \"" + std::string(Value::DescribeType(left.type())) +
421 "\" instead.");
422 return Value();
423 } else if (right.type() != Value::BOOLEAN) {
424 *err = Err(op_node->right(), "Right side of || operator is not a boolean.",
425 "Type is \"" + std::string(Value::DescribeType(right.type())) +
426 "\" instead.");
427 return Value();
429 return Value(op_node, left.boolean_value() || right.boolean_value());
432 Value ExecuteAnd(Scope* scope,
433 const BinaryOpNode* op_node,
434 const Value& left,
435 const Value& right,
436 Err* err) {
437 if (left.type() != Value::BOOLEAN) {
438 *err = Err(op_node->left(), "Left side of && operator is not a boolean.",
439 "Type is \"" + std::string(Value::DescribeType(left.type())) +
440 "\" instead.");
441 return Value();
442 } else if (right.type() != Value::BOOLEAN) {
443 *err = Err(op_node->right(), "Right side of && operator is not a boolean.",
444 "Type is \"" + std::string(Value::DescribeType(right.type())) +
445 "\" instead.");
446 return Value();
448 return Value(op_node, left.boolean_value() && right.boolean_value());
451 } // namespace
453 // ----------------------------------------------------------------------------
455 bool IsUnaryOperator(const Token& token) {
456 return token.type() == Token::BANG;
459 bool IsBinaryOperator(const Token& token) {
460 return token.type() == Token::EQUAL ||
461 token.type() == Token::PLUS ||
462 token.type() == Token::MINUS ||
463 token.type() == Token::PLUS_EQUALS ||
464 token.type() == Token::MINUS_EQUALS ||
465 token.type() == Token::EQUAL_EQUAL ||
466 token.type() == Token::NOT_EQUAL ||
467 token.type() == Token::LESS_EQUAL ||
468 token.type() == Token::GREATER_EQUAL ||
469 token.type() == Token::LESS_THAN ||
470 token.type() == Token::GREATER_THAN ||
471 token.type() == Token::BOOLEAN_AND ||
472 token.type() == Token::BOOLEAN_OR;
475 bool IsFunctionCallArgBeginScoper(const Token& token) {
476 return token.type() == Token::LEFT_PAREN;
479 bool IsFunctionCallArgEndScoper(const Token& token) {
480 return token.type() == Token::RIGHT_PAREN;
483 bool IsScopeBeginScoper(const Token& token) {
484 return token.type() == Token::LEFT_BRACE;
487 bool IsScopeEndScoper(const Token& token) {
488 return token.type() == Token::RIGHT_BRACE;
491 Value ExecuteUnaryOperator(Scope* scope,
492 const UnaryOpNode* op_node,
493 const Value& expr,
494 Err* err) {
495 DCHECK(op_node->op().type() == Token::BANG);
497 if (expr.type() != Value::BOOLEAN) {
498 *err = Err(op_node, "Operand of ! operator is not a boolean.",
499 "Type is \"" + std::string(Value::DescribeType(expr.type())) +
500 "\" instead.");
501 return Value();
503 // TODO(scottmg): Why no unary minus?
504 return Value(op_node, !expr.boolean_value());
507 Value ExecuteBinaryOperator(Scope* scope,
508 const BinaryOpNode* op_node,
509 const ParseNode* left,
510 const ParseNode* right,
511 Err* err) {
512 const Token& op = op_node->op();
514 // First handle the ones that take an lvalue.
515 if (op.type() == Token::EQUAL ||
516 op.type() == Token::PLUS_EQUALS ||
517 op.type() == Token::MINUS_EQUALS) {
518 const IdentifierNode* left_id = left->AsIdentifier();
519 if (!left_id) {
520 *err = Err(op, "Operator requires a lvalue.",
521 "This thing on the left is not an identifier.");
522 err->AppendRange(left->GetRange());
523 return Value();
525 const Token& dest = left_id->value();
527 Value right_value = right->Execute(scope, err);
528 if (err->has_error())
529 return Value();
530 if (right_value.type() == Value::NONE) {
531 *err = Err(op, "Operator requires a rvalue.",
532 "This thing on the right does not evaluate to a value.");
533 err->AppendRange(right->GetRange());
534 return Value();
537 if (op.type() == Token::EQUAL)
538 return ExecuteEquals(scope, op_node, dest, right_value, err);
539 if (op.type() == Token::PLUS_EQUALS)
540 return ExecutePlusEquals(scope, op_node, dest, right_value, err);
541 if (op.type() == Token::MINUS_EQUALS)
542 return ExecuteMinusEquals(scope, op_node, dest, right_value, err);
543 NOTREACHED();
544 return Value();
547 // Left value.
548 Value left_value = left->Execute(scope, err);
549 if (err->has_error())
550 return Value();
551 if (left_value.type() == Value::NONE) {
552 *err = Err(op, "Operator requires a value.",
553 "This thing on the left does not evaluate to a value.");
554 err->AppendRange(left->GetRange());
555 return Value();
558 // Right value. Note: don't move this above to share code with the lvalue
559 // version since in this case we want to execute the left side first.
560 Value right_value = right->Execute(scope, err);
561 if (err->has_error())
562 return Value();
563 if (right_value.type() == Value::NONE) {
564 *err = Err(op, "Operator requires a value.",
565 "This thing on the right does not evaluate to a value.");
566 err->AppendRange(right->GetRange());
567 return Value();
570 // +, -.
571 if (op.type() == Token::MINUS)
572 return ExecuteMinus(scope, op_node, left_value, right_value, err);
573 if (op.type() == Token::PLUS)
574 return ExecutePlus(scope, op_node, left_value, right_value, err);
576 // Comparisons.
577 if (op.type() == Token::EQUAL_EQUAL)
578 return ExecuteEqualsEquals(scope, op_node, left_value, right_value, err);
579 if (op.type() == Token::NOT_EQUAL)
580 return ExecuteNotEquals(scope, op_node, left_value, right_value, err);
581 if (op.type() == Token::GREATER_EQUAL)
582 return ExecuteGreaterEquals(scope, op_node, left_value, right_value, err);
583 if (op.type() == Token::LESS_EQUAL)
584 return ExecuteLessEquals(scope, op_node, left_value, right_value, err);
585 if (op.type() == Token::GREATER_THAN)
586 return ExecuteGreater(scope, op_node, left_value, right_value, err);
587 if (op.type() == Token::LESS_THAN)
588 return ExecuteLess(scope, op_node, left_value, right_value, err);
590 // ||, &&.
591 if (op.type() == Token::BOOLEAN_OR)
592 return ExecuteOr(scope, op_node, left_value, right_value, err);
593 if (op.type() == Token::BOOLEAN_AND)
594 return ExecuteAnd(scope, op_node, left_value, right_value, err);
596 return Value();