// Copyright (c) 2013 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "tools/gn/value.h" #include #include #include "base/strings/string_number_conversions.h" #include "base/strings/string_util.h" #include "tools/gn/scope.h" Value::Value() : type_(NONE), boolean_value_(false), int_value_(0), origin_(nullptr) { } Value::Value(const ParseNode* origin, Type t) : type_(t), boolean_value_(false), int_value_(0), origin_(origin) { } Value::Value(const ParseNode* origin, bool bool_val) : type_(BOOLEAN), boolean_value_(bool_val), int_value_(0), origin_(origin) { } Value::Value(const ParseNode* origin, int64_t int_val) : type_(INTEGER), boolean_value_(false), int_value_(int_val), origin_(origin) { } Value::Value(const ParseNode* origin, std::string str_val) : type_(STRING), string_value_(std::move(str_val)), boolean_value_(false), int_value_(0), origin_(origin) {} Value::Value(const ParseNode* origin, const char* str_val) : type_(STRING), string_value_(str_val), boolean_value_(false), int_value_(0), origin_(origin) { } Value::Value(const ParseNode* origin, std::unique_ptr scope) : type_(SCOPE), string_value_(), boolean_value_(false), int_value_(0), scope_value_(std::move(scope)), origin_(origin) {} Value::Value(const Value& other) : type_(other.type_), string_value_(other.string_value_), boolean_value_(other.boolean_value_), int_value_(other.int_value_), list_value_(other.list_value_), origin_(other.origin_) { if (type() == SCOPE && other.scope_value_.get()) scope_value_ = other.scope_value_->MakeClosure(); } Value::Value(Value&& other) noexcept = default; Value::~Value() { } Value& Value::operator=(const Value& other) { type_ = other.type_; string_value_ = other.string_value_; boolean_value_ = other.boolean_value_; int_value_ = other.int_value_; list_value_ = other.list_value_; if (type() == SCOPE && other.scope_value_.get()) scope_value_ = other.scope_value_->MakeClosure(); origin_ = other.origin_; return *this; } // static const char* Value::DescribeType(Type t) { switch (t) { case NONE: return "none"; case BOOLEAN: return "boolean"; case INTEGER: return "integer"; case STRING: return "string"; case LIST: return "list"; case SCOPE: return "scope"; default: NOTREACHED(); return "UNKNOWN"; } } void Value::SetScopeValue(std::unique_ptr scope) { DCHECK(type_ == SCOPE); scope_value_ = std::move(scope); } std::string Value::ToString(bool quote_string) const { switch (type_) { case NONE: return ""; case BOOLEAN: return boolean_value_ ? "true" : "false"; case INTEGER: return base::Int64ToString(int_value_); case STRING: if (quote_string) { std::string result = "\""; bool hanging_backslash = false; for (char ch : string_value_) { // If the last character was a literal backslash and the next // character could form a valid escape sequence, we need to insert // an extra backslash to prevent that. if (hanging_backslash && (ch == '$' || ch == '"' || ch == '\\')) result += '\\'; // If the next character is a dollar sign or double quote, it needs // to be escaped; otherwise it can be printed as is. if (ch == '$' || ch == '"') result += '\\'; result += ch; hanging_backslash = (ch == '\\'); } // Again, we need to prevent the closing double quotes from becoming // an escape sequence. if (hanging_backslash) result += '\\'; result += '"'; return result; } return string_value_; case LIST: { std::string result = "["; for (size_t i = 0; i < list_value_.size(); i++) { if (i > 0) result += ", "; result += list_value_[i].ToString(true); } result.push_back(']'); return result; } case SCOPE: { Scope::KeyValueMap scope_values; scope_value_->GetCurrentScopeValues(&scope_values); if (scope_values.empty()) return std::string("{ }"); std::string result = "{\n"; for (const auto& pair : scope_values) { result += " " + pair.first.as_string() + " = " + pair.second.ToString(true) + "\n"; } result += "}"; return result; } } return std::string(); } bool Value::VerifyTypeIs(Type t, Err* err) const { if (type_ == t) return true; *err = Err(origin(), std::string("This is not a ") + DescribeType(t) + ".", std::string("Instead I see a ") + DescribeType(type_) + " = " + ToString(true)); return false; } bool Value::operator==(const Value& other) const { if (type_ != other.type_) return false; switch (type_) { case Value::BOOLEAN: return boolean_value() == other.boolean_value(); case Value::INTEGER: return int_value() == other.int_value(); case Value::STRING: return string_value() == other.string_value(); case Value::LIST: if (list_value().size() != other.list_value().size()) return false; for (size_t i = 0; i < list_value().size(); i++) { if (list_value()[i] != other.list_value()[i]) return false; } return true; case Value::SCOPE: // Scopes are always considered not equal because there's currently // no use case for comparing them, and it requires a bunch of complex // iteration code. return false; default: return false; } } bool Value::operator!=(const Value& other) const { return !operator==(other); }