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"
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
21 void AppendFilteredSourcesToValue(const Scope
* scope
,
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
);
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
);
39 if (!filter
|| filter
->is_empty()) {
40 // No filter, append everything.
41 for (const auto& source_entry
: source
.list_value())
42 dest
->list_value().push_back(source_entry
);
46 // Note: don't reserve() the dest vector here since that actually hurts
47 // the allocation pattern when the build script is doing multiple small
49 for (const auto& source_entry
: source
.list_value()) {
50 if (!filter
->MatchesValue(source_entry
))
51 dest
->list_value().push_back(source_entry
);
55 Value
GetValueOrFillError(const BinaryOpNode
* op_node
,
56 const ParseNode
* node
,
60 Value value
= node
->Execute(scope
, err
);
63 if (value
.type() == Value::NONE
) {
64 *err
= Err(op_node
->op(),
65 "Operator requires a value.",
66 "This thing on the " + std::string(name
) +
67 " does not evaluate to a value.");
68 err
->AppendRange(node
->GetRange());
74 void RemoveMatchesFromList(const BinaryOpNode
* op_node
,
76 const Value
& to_remove
,
78 std::vector
<Value
>& v
= list
->list_value();
79 switch (to_remove
.type()) {
81 case Value::INTEGER
: // Filter out the individual int/string.
83 bool found_match
= false;
84 for (size_t i
= 0; i
< v
.size(); /* nothing */) {
85 if (v
[i
] == to_remove
) {
87 v
.erase(v
.begin() + i
);
93 *err
= Err(to_remove
.origin()->GetRange(), "Item not found",
94 "You were trying to remove " + to_remove
.ToString(true) +
95 "\nfrom the list but it wasn't there.");
100 case Value::LIST
: // Filter out each individual thing.
101 for (const auto& elem
: to_remove
.list_value()) {
102 // TODO(brettw) if the nested item is a list, we may want to search
103 // for the literal list rather than remote the items in it.
104 RemoveMatchesFromList(op_node
, list
, elem
, err
);
105 if (err
->has_error())
115 // Assignment -----------------------------------------------------------------
117 // We return a null value from this rather than the result of doing the append.
118 // See ValuePlusEquals for rationale.
119 Value
ExecuteEquals(Scope
* scope
,
120 const BinaryOpNode
* op_node
,
124 const Value
* old_value
= scope
->GetValue(left
.value(), false);
126 // Throw an error when overwriting a nonempty list with another nonempty
127 // list item. This is to detect the case where you write
129 // and you overwrote inherited ones, when instead you mean to append:
130 // defines += ["FOO"]
131 if (old_value
->type() == Value::LIST
&&
132 !old_value
->list_value().empty() &&
133 right
.type() == Value::LIST
&&
134 !right
.list_value().empty()) {
135 *err
= Err(op_node
->left()->GetRange(), "Replacing nonempty list.",
136 std::string("This overwrites a previously-defined nonempty list ") +
138 base::IntToString(static_cast<int>(old_value
->list_value().size()))
140 err
->AppendSubErr(Err(*old_value
, "for previous definition",
141 "with another one (length " +
142 base::IntToString(static_cast<int>(right
.list_value().size())) +
144 "\"+=\" to append instead? If you\nreally want to do this, do\n " +
145 left
.value().as_string() + " = []\nbefore reassigning."));
149 if (err
->has_error())
152 if (right
.type() == Value::LIST
&& left
.value() == kSourcesName
) {
153 // Assigning to sources, filter the list. Here we do the filtering and
154 // copying in one step to save an extra list copy (the lists may be
156 Value
* set_value
= scope
->SetValue(left
.value(),
157 Value(op_node
, Value::LIST
), op_node
);
158 set_value
->list_value().reserve(right
.list_value().size());
159 AppendFilteredSourcesToValue(scope
, right
, set_value
);
161 // Normal value set, just copy it.
162 scope
->SetValue(left
.value(), right
, op_node
->right());
167 // allow_type_conversion indicates if we're allowed to change the type of the
168 // left value. This is set to true when doing +, and false when doing +=.
170 // Note that we return Value() from here, which is different than C++. This
171 // means you can't do clever things like foo = [ bar += baz ] to simultaneously
172 // append to and use a value. This is basically never needed in out build
173 // scripts and is just as likely an error as the intended behavior, and it also
174 // involves a copy of the value when it's returned. Many times we're appending
175 // to large lists, and copying the value to discard it for the next statement
177 void ValuePlusEquals(const Scope
* scope
,
178 const BinaryOpNode
* op_node
,
179 const Token
& left_token
,
182 bool allow_type_conversion
,
184 switch (left
->type()) {
185 // Left-hand-side int.
187 switch (right
.type()) {
188 case Value::INTEGER
: // int + int -> addition.
189 left
->int_value() += right
.int_value();
192 case Value::STRING
: // int + string -> string concat.
193 if (allow_type_conversion
) {
194 *left
= Value(op_node
,
195 base::Int64ToString(left
->int_value()) + right
.string_value());
205 // Left-hand-side string.
207 switch (right
.type()) {
208 case Value::INTEGER
: // string + int -> string concat.
209 left
->string_value().append(base::Int64ToString(right
.int_value()));
212 case Value::STRING
: // string + string -> string contat.
213 left
->string_value().append(right
.string_value());
221 // Left-hand-side list.
223 switch (right
.type()) {
224 case Value::LIST
: // list + list -> list concat.
225 if (left_token
.value() == kSourcesName
) {
226 // Filter additions through the assignment filter.
227 AppendFilteredSourcesToValue(scope
, right
, left
);
229 // Normal list concat.
230 for (const auto& value
: right
.list_value())
231 left
->list_value().push_back(value
);
236 *err
= Err(op_node
->op(), "Incompatible types to add.",
237 "To append a single item to a list do \"foo += [ bar ]\".");
245 *err
= Err(op_node
->op(), "Incompatible types to add.",
246 std::string("I see a ") + Value::DescribeType(left
->type()) + " and a " +
247 Value::DescribeType(right
.type()) + ".");
250 Value
ExecutePlusEquals(Scope
* scope
,
251 const BinaryOpNode
* op_node
,
255 // We modify in-place rather than doing read-modify-write to avoid
256 // copying large lists.
258 scope
->GetValueForcedToCurrentScope(left
.value(), op_node
);
260 *err
= Err(left
, "Undefined variable for +=.",
261 "I don't have something with this name in scope now.");
264 ValuePlusEquals(scope
, op_node
, left
, left_value
, right
, false, err
);
265 left_value
->set_origin(op_node
);
266 scope
->MarkUnused(left
.value());
270 // We return a null value from this rather than the result of doing the append.
271 // See ValuePlusEquals for rationale.
272 void ValueMinusEquals(const BinaryOpNode
* op_node
,
275 bool allow_type_conversion
,
277 switch (left
->type()) {
278 // Left-hand-side int.
280 switch (right
.type()) {
281 case Value::INTEGER
: // int - int -> subtraction.
282 left
->int_value() -= right
.int_value();
290 // Left-hand-side string.
292 break; // All are errors.
294 // Left-hand-side list.
296 if (right
.type() != Value::LIST
) {
297 *err
= Err(op_node
->op(), "Incompatible types to subtract.",
298 "To remove a single item from a list do \"foo -= [ bar ]\".");
300 RemoveMatchesFromList(op_node
, left
, right
, err
);
308 *err
= Err(op_node
->op(), "Incompatible types to subtract.",
309 std::string("I see a ") + Value::DescribeType(left
->type()) + " and a " +
310 Value::DescribeType(right
.type()) + ".");
313 Value
ExecuteMinusEquals(Scope
* scope
,
314 const BinaryOpNode
* op_node
,
319 scope
->GetValueForcedToCurrentScope(left
.value(), op_node
);
321 *err
= Err(left
, "Undefined variable for -=.",
322 "I don't have something with this name in scope now.");
325 ValueMinusEquals(op_node
, left_value
, right
, false, err
);
326 left_value
->set_origin(op_node
);
327 scope
->MarkUnused(left
.value());
331 // Plus/Minus -----------------------------------------------------------------
333 Value
ExecutePlus(Scope
* scope
,
334 const BinaryOpNode
* op_node
,
339 ValuePlusEquals(scope
, op_node
, Token(), &ret
, right
, true, err
);
340 ret
.set_origin(op_node
);
344 Value
ExecuteMinus(Scope
* scope
,
345 const BinaryOpNode
* op_node
,
350 ValueMinusEquals(op_node
, &ret
, right
, true, err
);
351 ret
.set_origin(op_node
);
355 // Comparison -----------------------------------------------------------------
357 Value
ExecuteEqualsEquals(Scope
* scope
,
358 const BinaryOpNode
* op_node
,
363 return Value(op_node
, true);
364 return Value(op_node
, false);
367 Value
ExecuteNotEquals(Scope
* scope
,
368 const BinaryOpNode
* op_node
,
372 // Evaluate in terms of ==.
373 Value result
= ExecuteEqualsEquals(scope
, op_node
, left
, right
, err
);
374 result
.boolean_value() = !result
.boolean_value();
378 Value
FillNeedsTwoIntegersError(const BinaryOpNode
* op_node
,
382 *err
= Err(op_node
, "Comparison requires two integers.",
383 "This operator can only compare two integers.");
384 err
->AppendRange(left
.origin()->GetRange());
385 err
->AppendRange(right
.origin()->GetRange());
389 Value
ExecuteLessEquals(Scope
* scope
,
390 const BinaryOpNode
* op_node
,
394 if (left
.type() != Value::INTEGER
|| right
.type() != Value::INTEGER
)
395 return FillNeedsTwoIntegersError(op_node
, left
, right
, err
);
396 return Value(op_node
, left
.int_value() <= right
.int_value());
399 Value
ExecuteGreaterEquals(Scope
* scope
,
400 const BinaryOpNode
* op_node
,
404 if (left
.type() != Value::INTEGER
|| right
.type() != Value::INTEGER
)
405 return FillNeedsTwoIntegersError(op_node
, left
, right
, err
);
406 return Value(op_node
, left
.int_value() >= right
.int_value());
409 Value
ExecuteGreater(Scope
* scope
,
410 const BinaryOpNode
* op_node
,
414 if (left
.type() != Value::INTEGER
|| right
.type() != Value::INTEGER
)
415 return FillNeedsTwoIntegersError(op_node
, left
, right
, err
);
416 return Value(op_node
, left
.int_value() > right
.int_value());
419 Value
ExecuteLess(Scope
* scope
,
420 const BinaryOpNode
* op_node
,
424 if (left
.type() != Value::INTEGER
|| right
.type() != Value::INTEGER
)
425 return FillNeedsTwoIntegersError(op_node
, left
, right
, err
);
426 return Value(op_node
, left
.int_value() < right
.int_value());
429 // Binary ----------------------------------------------------------------------
431 Value
ExecuteOr(Scope
* scope
,
432 const BinaryOpNode
* op_node
,
433 const ParseNode
* left_node
,
434 const ParseNode
* right_node
,
436 Value left
= GetValueOrFillError(op_node
, left_node
, "left", scope
, err
);
437 if (err
->has_error())
439 if (left
.type() != Value::BOOLEAN
) {
440 *err
= Err(op_node
->left(), "Left side of || operator is not a boolean.",
441 "Type is \"" + std::string(Value::DescribeType(left
.type())) +
445 if (left
.boolean_value())
446 return Value(op_node
, left
.boolean_value());
448 Value right
= GetValueOrFillError(op_node
, right_node
, "right", scope
, err
);
449 if (err
->has_error())
451 if (right
.type() != Value::BOOLEAN
) {
452 *err
= Err(op_node
->right(), "Right side of || operator is not a boolean.",
453 "Type is \"" + std::string(Value::DescribeType(right
.type())) +
458 return Value(op_node
, left
.boolean_value() || right
.boolean_value());
461 Value
ExecuteAnd(Scope
* scope
,
462 const BinaryOpNode
* op_node
,
463 const ParseNode
* left_node
,
464 const ParseNode
* right_node
,
466 Value left
= GetValueOrFillError(op_node
, left_node
, "left", scope
, err
);
467 if (err
->has_error())
469 if (left
.type() != Value::BOOLEAN
) {
470 *err
= Err(op_node
->left(), "Left side of && operator is not a boolean.",
471 "Type is \"" + std::string(Value::DescribeType(left
.type())) +
475 if (!left
.boolean_value())
476 return Value(op_node
, left
.boolean_value());
478 Value right
= GetValueOrFillError(op_node
, right_node
, "right", scope
, err
);
479 if (err
->has_error())
481 if (right
.type() != Value::BOOLEAN
) {
482 *err
= Err(op_node
->right(), "Right side of && operator is not a boolean.",
483 "Type is \"" + std::string(Value::DescribeType(right
.type())) +
487 return Value(op_node
, left
.boolean_value() && right
.boolean_value());
492 // ----------------------------------------------------------------------------
494 Value
ExecuteUnaryOperator(Scope
* scope
,
495 const UnaryOpNode
* op_node
,
498 DCHECK(op_node
->op().type() == Token::BANG
);
500 if (expr
.type() != Value::BOOLEAN
) {
501 *err
= Err(op_node
, "Operand of ! operator is not a boolean.",
502 "Type is \"" + std::string(Value::DescribeType(expr
.type())) +
506 // TODO(scottmg): Why no unary minus?
507 return Value(op_node
, !expr
.boolean_value());
510 Value
ExecuteBinaryOperator(Scope
* scope
,
511 const BinaryOpNode
* op_node
,
512 const ParseNode
* left
,
513 const ParseNode
* right
,
515 const Token
& op
= op_node
->op();
517 // First handle the ones that take an lvalue.
518 if (op
.type() == Token::EQUAL
||
519 op
.type() == Token::PLUS_EQUALS
||
520 op
.type() == Token::MINUS_EQUALS
) {
521 const IdentifierNode
* left_id
= left
->AsIdentifier();
523 *err
= Err(op
, "Operator requires a lvalue.",
524 "This thing on the left is not an identifier.");
525 err
->AppendRange(left
->GetRange());
528 const Token
& dest
= left_id
->value();
530 Value right_value
= right
->Execute(scope
, err
);
531 if (err
->has_error())
533 if (right_value
.type() == Value::NONE
) {
534 *err
= Err(op
, "Operator requires a rvalue.",
535 "This thing on the right does not evaluate to a value.");
536 err
->AppendRange(right
->GetRange());
540 if (op
.type() == Token::EQUAL
)
541 return ExecuteEquals(scope
, op_node
, dest
, right_value
, err
);
542 if (op
.type() == Token::PLUS_EQUALS
)
543 return ExecutePlusEquals(scope
, op_node
, dest
, right_value
, err
);
544 if (op
.type() == Token::MINUS_EQUALS
)
545 return ExecuteMinusEquals(scope
, op_node
, dest
, right_value
, err
);
550 // ||, &&. Passed the node instead of the value so that they can avoid
551 // evaluating the RHS on early-out.
552 if (op
.type() == Token::BOOLEAN_OR
)
553 return ExecuteOr(scope
, op_node
, left
, right
, err
);
554 if (op
.type() == Token::BOOLEAN_AND
)
555 return ExecuteAnd(scope
, op_node
, left
, right
, err
);
557 Value left_value
= GetValueOrFillError(op_node
, left
, "left", scope
, err
);
558 if (err
->has_error())
560 Value right_value
= GetValueOrFillError(op_node
, right
, "right", scope
, err
);
561 if (err
->has_error())
565 if (op
.type() == Token::MINUS
)
566 return ExecuteMinus(scope
, op_node
, left_value
, right_value
, err
);
567 if (op
.type() == Token::PLUS
)
568 return ExecutePlus(scope
, op_node
, left_value
, right_value
, err
);
571 if (op
.type() == Token::EQUAL_EQUAL
)
572 return ExecuteEqualsEquals(scope
, op_node
, left_value
, right_value
, err
);
573 if (op
.type() == Token::NOT_EQUAL
)
574 return ExecuteNotEquals(scope
, op_node
, left_value
, right_value
, err
);
575 if (op
.type() == Token::GREATER_EQUAL
)
576 return ExecuteGreaterEquals(scope
, op_node
, left_value
, right_value
, err
);
577 if (op
.type() == Token::LESS_EQUAL
)
578 return ExecuteLessEquals(scope
, op_node
, left_value
, right_value
, err
);
579 if (op
.type() == Token::GREATER_THAN
)
580 return ExecuteGreater(scope
, op_node
, left_value
, right_value
, err
);
581 if (op
.type() == Token::LESS_THAN
)
582 return ExecuteLess(scope
, op_node
, left_value
, right_value
, err
);