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 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
]);
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
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
,
58 const Value
& to_remove
,
60 std::vector
<Value
>& v
= list
->list_value();
61 switch (to_remove
.type()) {
63 case Value::INTEGER
: // Filter out the individual int/string.
65 bool found_match
= false;
66 for (size_t i
= 0; i
< v
.size(); /* nothing */) {
67 if (v
[i
] == to_remove
) {
69 v
.erase(v
.begin() + i
);
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.");
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
);
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
,
106 const Value
* old_value
= scope
->GetValue(left
.value(), false);
108 // Throw an error when overwriting a nonempty list with another nonempty
109 // list item. This is to detect the case where you write
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 ") +
120 base::IntToString(static_cast<int>(old_value
->list_value().size()))
122 err
->AppendSubErr(Err(*old_value
, "for previous definition",
123 "with another one (length " +
124 base::IntToString(static_cast<int>(right
.list_value().size())) +
126 "\"+=\" to append instead? If you\nreally want to do this, do\n " +
127 left
.value().as_string() + " = []\nbefore reassigning."));
131 if (err
->has_error())
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
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
);
143 // Normal value set, just copy it.
144 scope
->SetValue(left
.value(), right
, op_node
->right());
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
159 void ValuePlusEquals(const Scope
* scope
,
160 const BinaryOpNode
* op_node
,
161 const Token
& left_token
,
164 bool allow_type_conversion
,
166 switch (left
->type()) {
167 // Left-hand-side int.
169 switch (right
.type()) {
170 case Value::INTEGER
: // int + int -> addition.
171 left
->int_value() += right
.int_value();
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());
187 // Left-hand-side string.
189 switch (right
.type()) {
190 case Value::INTEGER
: // string + int -> string concat.
191 left
->string_value().append(base::Int64ToString(right
.int_value()));
194 case Value::STRING
: // string + string -> string contat.
195 left
->string_value().append(right
.string_value());
203 // Left-hand-side 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
);
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
]);
218 *err
= Err(op_node
->op(), "Incompatible types to add.",
219 "To append a single item to a list do \"foo += [ bar ]\".");
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
,
237 // We modify in-place rather than doing read-modify-write to avoid
238 // copying large lists.
240 scope
->GetValueForcedToCurrentScope(left
.value(), op_node
);
242 *err
= Err(left
, "Undefined variable for +=.",
243 "I don't have something with this name in scope now.");
246 ValuePlusEquals(scope
, op_node
, left
, left_value
, right
, false, err
);
247 left_value
->set_origin(op_node
);
248 scope
->MarkUnused(left
.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
,
257 bool allow_type_conversion
,
259 switch (left
->type()) {
260 // Left-hand-side int.
262 switch (right
.type()) {
263 case Value::INTEGER
: // int - int -> subtraction.
264 left
->int_value() -= right
.int_value();
272 // Left-hand-side string.
274 break; // All are errors.
276 // Left-hand-side 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 ]\".");
282 RemoveMatchesFromList(op_node
, left
, right
, err
);
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
,
301 scope
->GetValueForcedToCurrentScope(left
.value(), op_node
);
303 *err
= Err(left
, "Undefined variable for -=.",
304 "I don't have something with this name in scope now.");
307 ValueMinusEquals(op_node
, left_value
, right
, false, err
);
308 left_value
->set_origin(op_node
);
309 scope
->MarkUnused(left
.value());
313 // Plus/Minus -----------------------------------------------------------------
315 Value
ExecutePlus(Scope
* scope
,
316 const BinaryOpNode
* op_node
,
321 ValuePlusEquals(scope
, op_node
, Token(), &ret
, right
, true, err
);
322 ret
.set_origin(op_node
);
326 Value
ExecuteMinus(Scope
* scope
,
327 const BinaryOpNode
* op_node
,
332 ValueMinusEquals(op_node
, &ret
, right
, true, err
);
333 ret
.set_origin(op_node
);
337 // Comparison -----------------------------------------------------------------
339 Value
ExecuteEqualsEquals(Scope
* scope
,
340 const BinaryOpNode
* op_node
,
345 return Value(op_node
, true);
346 return Value(op_node
, false);
349 Value
ExecuteNotEquals(Scope
* scope
,
350 const BinaryOpNode
* op_node
,
354 // Evaluate in terms of ==.
355 Value result
= ExecuteEqualsEquals(scope
, op_node
, left
, right
, err
);
356 result
.boolean_value() = !result
.boolean_value();
360 Value
FillNeedsTwoIntegersError(const BinaryOpNode
* op_node
,
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());
371 Value
ExecuteLessEquals(Scope
* scope
,
372 const BinaryOpNode
* op_node
,
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
,
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
,
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
,
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
,
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())) +
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())) +
429 return Value(op_node
, left
.boolean_value() || right
.boolean_value());
432 Value
ExecuteAnd(Scope
* scope
,
433 const BinaryOpNode
* op_node
,
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())) +
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())) +
448 return Value(op_node
, left
.boolean_value() && right
.boolean_value());
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
,
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())) +
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
,
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();
520 *err
= Err(op
, "Operator requires a lvalue.",
521 "This thing on the left is not an identifier.");
522 err
->AppendRange(left
->GetRange());
525 const Token
& dest
= left_id
->value();
527 Value right_value
= right
->Execute(scope
, err
);
528 if (err
->has_error())
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());
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
);
548 Value left_value
= left
->Execute(scope
, err
);
549 if (err
->has_error())
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());
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())
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());
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
);
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
);
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
);