OLD | NEW |
| (Empty) |
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 "tools/gn/operators.h" | |
6 | |
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" | |
13 | |
14 namespace { | |
15 | |
16 const char kSourcesName[] = "sources"; | |
17 | |
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(); | |
25 | |
26 const std::vector<Value>& source_list = source.list_value(); | |
27 | |
28 if (source.type() == Value::STRING) { | |
29 if (!filter || filter->is_empty() || | |
30 !filter->MatchesValue(source)) | |
31 dest->list_value().push_back(source); | |
32 return; | |
33 } | |
34 | |
35 // Otherwise source is a list. | |
36 DCHECK(source.type() == Value::LIST); | |
37 if (!filter || filter->is_empty()) { | |
38 // No filter, append everything. | |
39 for (size_t i = 0; i < source_list.size(); i++) | |
40 dest->list_value().push_back(source_list[i]); | |
41 return; | |
42 } | |
43 | |
44 // Note: don't reserve() the dest vector here since that actually hurts | |
45 // the allocation pattern when the build script is doing multiple small | |
46 // additions. | |
47 for (size_t i = 0; i < source_list.size(); i++) { | |
48 if (!filter->MatchesValue(source_list[i])) | |
49 dest->list_value().push_back(source_list[i]); | |
50 } | |
51 } | |
52 | |
53 void RemoveMatchesFromList(const BinaryOpNode* op_node, | |
54 Value* list, | |
55 const Value& to_remove, | |
56 Err* err) { | |
57 std::vector<Value>& v = list->list_value(); | |
58 switch (to_remove.type()) { | |
59 case Value::INTEGER: // Filter out the individual int/string. | |
60 case Value::STRING: { | |
61 bool found_match = false; | |
62 for (size_t i = 0; i < v.size(); /* nothing */) { | |
63 if (v[i] == to_remove) { | |
64 found_match = true; | |
65 v.erase(v.begin() + i); | |
66 } else { | |
67 i++; | |
68 } | |
69 } | |
70 if (!found_match) { | |
71 *err = Err(to_remove.origin()->GetRange(), "Item not found", | |
72 "You were trying to remove \"" + to_remove.ToString() + | |
73 "\"\nfrom the list but it wasn't there."); | |
74 } | |
75 break; | |
76 } | |
77 | |
78 case Value::LIST: // Filter out each individual thing. | |
79 for (size_t i = 0; i < to_remove.list_value().size(); i++) { | |
80 // TODO(brettw) if the nested item is a list, we may want to search | |
81 // for the literal list rather than remote the items in it. | |
82 RemoveMatchesFromList(op_node, list, to_remove.list_value()[i], err); | |
83 if (err->has_error()) | |
84 return; | |
85 } | |
86 break; | |
87 | |
88 default: | |
89 break; | |
90 } | |
91 } | |
92 | |
93 // Assignment ----------------------------------------------------------------- | |
94 | |
95 Value ExecuteEquals(Scope* scope, | |
96 const BinaryOpNode* op_node, | |
97 const Token& left, | |
98 const Value& right, | |
99 Err* err) { | |
100 const Value* old_value = scope->GetValue(left.value(), false); | |
101 if (old_value) { | |
102 if (scope->IsSetButUnused(left.value())) { | |
103 // Throw an error for re-assigning without using the value first. The | |
104 // exception is that you can overwrite an empty list with another list | |
105 // since this is the way to get around the "can't overwrite a nonempty | |
106 // list with another nonempty list" restriction. | |
107 if (old_value->type() != Value::LIST || | |
108 !old_value->list_value().empty()) { | |
109 *err = Err(op_node->left()->GetRange(), "Overwriting unused variable.", | |
110 "This overwrites a previous assignment to \"" + | |
111 left.value().as_string() + "\" that had no effect."); | |
112 err->AppendSubErr(Err(*scope->GetValue(left.value()), | |
113 "Previously set here.", | |
114 "Maybe you wanted \"+=\" to append instead?")); | |
115 return Value(); | |
116 } | |
117 } else { | |
118 // Throw an error when overwriting a nonempty list with another nonempty | |
119 // list item. This is to detect the case where you write | |
120 // defines = ["FOO"] | |
121 // and you overwrote inherited ones, when instead you mean to append: | |
122 // defines += ["FOO"] | |
123 if (old_value->type() == Value::LIST && | |
124 !old_value->list_value().empty() && | |
125 right.type() == Value::LIST && | |
126 !right.list_value().empty()) { | |
127 *err = Err(op_node->left()->GetRange(), "Replacing nonempty list.", | |
128 std::string("This overwrites a previously-defined nonempty list ") + | |
129 "(length " + base::IntToString(old_value->list_value().size()) + | |
130 ")."); | |
131 err->AppendSubErr(Err(*old_value, "for previous definition", | |
132 "with another one (length " + | |
133 base::IntToString(right.list_value().size()) + "). Did you mean " + | |
134 "\"+=\" to append instead? If you\nreally want to do this, do\n " + | |
135 left.value().as_string() + " = []\nbefore reassigning.")); | |
136 return Value(); | |
137 } | |
138 } | |
139 } | |
140 if (err->has_error()) | |
141 return Value(); | |
142 | |
143 if (right.type() == Value::LIST && left.value() == kSourcesName) { | |
144 // Assigning to sources, filter the list. Here we do the filtering and | |
145 // copying in one step to save an extra list copy (the lists may be | |
146 // long). | |
147 Value* set_value = scope->SetValue(left.value(), | |
148 Value(op_node, Value::LIST), op_node); | |
149 set_value->list_value().reserve(right.list_value().size()); | |
150 AppendFilteredSourcesToValue(scope, right, set_value); | |
151 } else { | |
152 // Normal value set, just copy it. | |
153 scope->SetValue(left.value(), right, op_node->right()); | |
154 } | |
155 return Value(); | |
156 } | |
157 | |
158 // allow_type_conversion indicates if we're allowed to change the type of the | |
159 // left value. This is set to true when doing +, and false when doing +=. | |
160 void ValuePlusEquals(const Scope* scope, | |
161 const BinaryOpNode* op_node, | |
162 const Token& left_token, | |
163 Value* left, | |
164 const Value& right, | |
165 bool allow_type_conversion, | |
166 Err* err) { | |
167 switch (left->type()) { | |
168 // Left-hand-side int. | |
169 case Value::INTEGER: | |
170 switch (right.type()) { | |
171 case Value::INTEGER: // int + int -> addition. | |
172 left->int_value() += right.int_value(); | |
173 return; | |
174 | |
175 case Value::STRING: // int + string -> string concat. | |
176 if (allow_type_conversion) { | |
177 *left = Value(op_node, | |
178 base::Int64ToString(left->int_value()) + right.string_value()); | |
179 return; | |
180 } | |
181 break; | |
182 | |
183 default: | |
184 break; | |
185 } | |
186 break; | |
187 | |
188 // Left-hand-side string. | |
189 case Value::STRING: | |
190 switch (right.type()) { | |
191 case Value::INTEGER: // string + int -> string concat. | |
192 left->string_value().append(base::Int64ToString(right.int_value())); | |
193 return; | |
194 | |
195 case Value::STRING: // string + string -> string contat. | |
196 left->string_value().append(right.string_value()); | |
197 return; | |
198 | |
199 default: | |
200 break; | |
201 } | |
202 break; | |
203 | |
204 // Left-hand-side list. | |
205 case Value::LIST: | |
206 switch (right.type()) { | |
207 case Value::INTEGER: // list + integer -> list append. | |
208 case Value::STRING: // list + string -> list append. | |
209 if (left_token.value() == kSourcesName) | |
210 AppendFilteredSourcesToValue(scope, right, left); | |
211 else | |
212 left->list_value().push_back(right); | |
213 return; | |
214 | |
215 case Value::LIST: // list + list -> list concat. | |
216 if (left_token.value() == kSourcesName) { | |
217 // Filter additions through the assignment filter. | |
218 AppendFilteredSourcesToValue(scope, right, left); | |
219 } else { | |
220 // Normal list concat. | |
221 for (size_t i = 0; i < right.list_value().size(); i++) | |
222 left->list_value().push_back(right.list_value()[i]); | |
223 } | |
224 return; | |
225 | |
226 default: | |
227 break; | |
228 } | |
229 | |
230 default: | |
231 break; | |
232 } | |
233 | |
234 *err = Err(op_node->op(), "Incompatible types to add.", | |
235 std::string("I see a ") + Value::DescribeType(left->type()) + " and a " + | |
236 Value::DescribeType(right.type()) + "."); | |
237 } | |
238 | |
239 Value ExecutePlusEquals(Scope* scope, | |
240 const BinaryOpNode* op_node, | |
241 const Token& left, | |
242 const Value& right, | |
243 Err* err) { | |
244 // We modify in-place rather than doing read-modify-write to avoid | |
245 // copying large lists. | |
246 Value* left_value = | |
247 scope->GetValueForcedToCurrentScope(left.value(), op_node); | |
248 if (!left_value) { | |
249 *err = Err(left, "Undefined variable for +=.", | |
250 "I don't have something with this name in scope now."); | |
251 return Value(); | |
252 } | |
253 ValuePlusEquals(scope, op_node, left, left_value, right, false, err); | |
254 left_value->set_origin(op_node); | |
255 scope->MarkUnused(left.value()); | |
256 return Value(); | |
257 } | |
258 | |
259 void ValueMinusEquals(const BinaryOpNode* op_node, | |
260 Value* left, | |
261 const Value& right, | |
262 bool allow_type_conversion, | |
263 Err* err) { | |
264 switch (left->type()) { | |
265 // Left-hand-side int. | |
266 case Value::INTEGER: | |
267 switch (right.type()) { | |
268 case Value::INTEGER: // int - int -> subtraction. | |
269 left->int_value() -= right.int_value(); | |
270 return; | |
271 | |
272 default: | |
273 break; | |
274 } | |
275 break; | |
276 | |
277 // Left-hand-side string. | |
278 case Value::STRING: | |
279 break; // All are errors. | |
280 | |
281 // Left-hand-side list. | |
282 case Value::LIST: | |
283 RemoveMatchesFromList(op_node, left, right, err); | |
284 return; | |
285 | |
286 default: | |
287 break; | |
288 } | |
289 | |
290 *err = Err(op_node->op(), "Incompatible types to add.", | |
291 std::string("I see a ") + Value::DescribeType(left->type()) + " and a " + | |
292 Value::DescribeType(right.type()) + "."); | |
293 } | |
294 | |
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(); | |
306 } | |
307 ValueMinusEquals(op_node, left_value, right, false, err); | |
308 left_value->set_origin(op_node); | |
309 scope->MarkUnused(left.value()); | |
310 return Value(); | |
311 } | |
312 | |
313 // Plus/Minus ----------------------------------------------------------------- | |
314 | |
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; | |
324 } | |
325 | |
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; | |
335 } | |
336 | |
337 // Comparison ----------------------------------------------------------------- | |
338 | |
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, 1); | |
346 return Value(op_node, 0); | |
347 } | |
348 | |
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.int_value() = static_cast<int64>(!result.int_value()); | |
357 return result; | |
358 } | |
359 | |
360 Value FillNeedsToIntegersError(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(); | |
369 } | |
370 | |
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 FillNeedsToIntegersError(op_node, left, right, err); | |
378 return Value(op_node, left.int_value() <= right.int_value()); | |
379 } | |
380 | |
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 FillNeedsToIntegersError(op_node, left, right, err); | |
388 return Value(op_node, left.int_value() >= right.int_value()); | |
389 } | |
390 | |
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 FillNeedsToIntegersError(op_node, left, right, err); | |
398 return Value(op_node, left.int_value() > right.int_value()); | |
399 } | |
400 | |
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 FillNeedsToIntegersError(op_node, left, right, err); | |
408 return Value(op_node, left.int_value() < right.int_value()); | |
409 } | |
410 | |
411 // Binary ---------------------------------------------------------------------- | |
412 | |
413 Value ExecuteOr(Scope* scope, | |
414 const BinaryOpNode* op_node, | |
415 const Value& left, | |
416 const Value& right, | |
417 Err* err) { | |
418 return Value(op_node, | |
419 static_cast<int64>(left.InterpretAsInt() || right.InterpretAsInt())); | |
420 } | |
421 | |
422 Value ExecuteAnd(Scope* scope, | |
423 const BinaryOpNode* op_node, | |
424 const Value& left, | |
425 const Value& right, | |
426 Err* err) { | |
427 return Value(op_node, | |
428 static_cast<int64>(left.InterpretAsInt() && right.InterpretAsInt())); | |
429 } | |
430 | |
431 } // namespace | |
432 | |
433 // ---------------------------------------------------------------------------- | |
434 | |
435 bool IsUnaryOperator(const Token& token) { | |
436 if (token.type() != Token::OPERATOR) | |
437 return false; | |
438 return token.value() == "!"; | |
439 } | |
440 | |
441 bool IsBinaryOperator(const Token& token) { | |
442 if (token.type() != Token::OPERATOR) | |
443 return false; | |
444 return token.value() == "=" || | |
445 token.value() == "+=" || | |
446 token.value() == "-=" || | |
447 token.value() == "+" || | |
448 token.value() == "-" || | |
449 token.value() == "==" || | |
450 token.value() == "!=" || | |
451 token.value() == "<=" || | |
452 token.value() == ">=" || | |
453 token.value() == "<" || | |
454 token.value() == ">" || | |
455 token.value() == "&&" || | |
456 token.value() == "||"; | |
457 } | |
458 | |
459 bool IsFunctionCallArgBeginScoper(const Token& token) { | |
460 return token.IsScoperEqualTo("("); | |
461 } | |
462 | |
463 bool IsFunctionCallArgEndScoper(const Token& token) { | |
464 return token.IsScoperEqualTo(")"); | |
465 } | |
466 | |
467 bool IsScopeBeginScoper(const Token& token) { | |
468 return token.IsScoperEqualTo("{"); | |
469 } | |
470 | |
471 bool IsScopeEndScoper(const Token& token) { | |
472 return token.IsScoperEqualTo("}"); | |
473 } | |
474 | |
475 Value ExecuteUnaryOperator(Scope* scope, | |
476 const UnaryOpNode* op_node, | |
477 const Value& expr, | |
478 Err* err) { | |
479 DCHECK(op_node->op().IsOperatorEqualTo("!")); | |
480 return Value(op_node, !expr.InterpretAsInt()); | |
481 } | |
482 | |
483 Value ExecuteBinaryOperator(Scope* scope, | |
484 const BinaryOpNode* op_node, | |
485 const ParseNode* left, | |
486 const ParseNode* right, | |
487 Err* err) { | |
488 const Token& op = op_node->op(); | |
489 | |
490 // First handle the ones that take an lvalue. | |
491 if (op.IsOperatorEqualTo("=") || | |
492 op.IsOperatorEqualTo("+=") || | |
493 op.IsOperatorEqualTo("-=")) { | |
494 const IdentifierNode* left_id = left->AsIdentifier(); | |
495 if (!left_id) { | |
496 *err = Err(op, "Operator requires an lvalue.", | |
497 "This thing on the left is not an idenfitier."); | |
498 err->AppendRange(left->GetRange()); | |
499 return Value(); | |
500 } | |
501 const Token& dest = left_id->value(); | |
502 | |
503 Value right_value = right->Execute(scope, err); | |
504 if (err->has_error()) | |
505 return Value(); | |
506 if (right_value.type() == Value::NONE) { | |
507 *err = Err(op, "Operator requires an rvalue.", | |
508 "This thing on the right does not evaluate to a value."); | |
509 err->AppendRange(right->GetRange()); | |
510 return Value(); | |
511 } | |
512 | |
513 if (op.IsOperatorEqualTo("=")) | |
514 return ExecuteEquals(scope, op_node, dest, right_value, err); | |
515 if (op.IsOperatorEqualTo("+=")) | |
516 return ExecutePlusEquals(scope, op_node, dest, right_value, err); | |
517 if (op.IsOperatorEqualTo("-=")) | |
518 return ExecuteMinusEquals(scope, op_node, dest, right_value, err); | |
519 NOTREACHED(); | |
520 return Value(); | |
521 } | |
522 | |
523 // Left value. | |
524 Value left_value = left->Execute(scope, err); | |
525 if (err->has_error()) | |
526 return Value(); | |
527 if (left_value.type() == Value::NONE) { | |
528 *err = Err(op, "Operator requires an value.", | |
529 "This thing on the left does not evaluate to a value."); | |
530 err->AppendRange(left->GetRange()); | |
531 return Value(); | |
532 } | |
533 | |
534 // Right value. Note: don't move this above to share code with the lvalue | |
535 // version since in this case we want to execute the left side first. | |
536 Value right_value = right->Execute(scope, err); | |
537 if (err->has_error()) | |
538 return Value(); | |
539 if (right_value.type() == Value::NONE) { | |
540 *err = Err(op, "Operator requires an value.", | |
541 "This thing on the right does not evaluate to a value."); | |
542 err->AppendRange(right->GetRange()); | |
543 return Value(); | |
544 } | |
545 | |
546 // +, -. | |
547 if (op.IsOperatorEqualTo("-")) | |
548 return ExecuteMinus(scope, op_node, left_value, right_value, err); | |
549 if (op.IsOperatorEqualTo("+")) | |
550 return ExecutePlus(scope, op_node, left_value, right_value, err); | |
551 | |
552 // Comparisons. | |
553 if (op.IsOperatorEqualTo("==")) | |
554 return ExecuteEqualsEquals(scope, op_node, left_value, right_value, err); | |
555 if (op.IsOperatorEqualTo("!=")) | |
556 return ExecuteNotEquals(scope, op_node, left_value, right_value, err); | |
557 if (op.IsOperatorEqualTo(">=")) | |
558 return ExecuteGreaterEquals(scope, op_node, left_value, right_value, err); | |
559 if (op.IsOperatorEqualTo("<=")) | |
560 return ExecuteLessEquals(scope, op_node, left_value, right_value, err); | |
561 if (op.IsOperatorEqualTo(">")) | |
562 return ExecuteGreater(scope, op_node, left_value, right_value, err); | |
563 if (op.IsOperatorEqualTo("<")) | |
564 return ExecuteLess(scope, op_node, left_value, right_value, err); | |
565 | |
566 // ||, &&. | |
567 if (op.IsOperatorEqualTo("||")) | |
568 return ExecuteOr(scope, op_node, left_value, right_value, err); | |
569 if (op.IsOperatorEqualTo("&&")) | |
570 return ExecuteAnd(scope, op_node, left_value, right_value, err); | |
571 | |
572 return Value(); | |
573 } | |
OLD | NEW |