//===--- StringInterpolation.swift - String Interpolation -----------------===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // //===----------------------------------------------------------------------===// /// Represents a string literal with interpolations while it's being built up. /// /// You don't need to create an instance of this type directly. It's used by the /// compiler when you create a string using string interpolation. Instead, use /// string interpolation to create a new string by including values, literals, /// variables, or expressions enclosed in parentheses, prefixed by a /// backslash (`\(`...`)`). /// /// let price = 2 /// let number = 3 /// let message = """ /// If one cookie costs \(price) dollars, \ /// \(number) cookies cost \(price * number) dollars. /// """ /// print(message) /// // Prints "If one cookie costs 2 dollars, 3 cookies cost 6 dollars." /// /// When implementing an `ExpressibleByStringInterpolation` conformance, /// set the `StringInterpolation` associated type to /// `DefaultStringInterpolation` to get the same interpolation behavior as /// Swift's built-in `String` type and construct a `String` with the results. /// If you don't want the default behavior or don't want to construct a /// `String`, use a custom type conforming to `StringInterpolationProtocol` /// instead. /// /// Extending default string interpolation behavior /// =============================================== /// /// Code outside the standard library can extend string interpolation on /// `String` and many other common types by extending /// `DefaultStringInterpolation` and adding an `appendInterpolation(...)` /// method. For example: /// /// extension DefaultStringInterpolation { /// fileprivate mutating func appendInterpolation( /// escaped value: String, asASCII forceASCII: Bool = false) { /// for char in value.unicodeScalars { /// appendInterpolation(char.escaped(asASCII: forceASCII)) /// } /// } /// } /// /// print("Escaped string: \(escaped: string)") /// /// See `StringInterpolationProtocol` for details on `appendInterpolation` /// methods. /// /// `DefaultStringInterpolation` extensions should add only `mutating` members /// and should not copy `self` or capture it in an escaping closure. @frozen public struct DefaultStringInterpolation: StringInterpolationProtocol, Sendable { /// The string contents accumulated by this instance. @usableFromInline internal var _storage: String /// Creates a string interpolation with storage pre-sized for a literal /// with the indicated attributes. /// /// You don't need to call this initializer directly. It's used by the /// compiler when interpreting string interpolations. @inlinable public init(literalCapacity: Int, interpolationCount: Int) { let capacityPerInterpolation = 2 let initialCapacity = literalCapacity + interpolationCount * capacityPerInterpolation _storage = String._createEmpty(withInitialCapacity: initialCapacity) } /// Appends a literal segment of a string interpolation. /// /// You don't need to call this method directly. It's used by the compiler /// when interpreting string interpolations. @inlinable public mutating func appendLiteral(_ literal: String) { literal.write(to: &self) } /// Interpolates the given value's textual representation into the /// string literal being created. /// /// You don't need to call this method directly. It's used by the compiler /// when interpreting string interpolations. Instead, use string /// interpolation to create a new string by including values, literals, /// variables, or expressions enclosed in parentheses, prefixed by a /// backslash (`\(`...`)`). /// /// let price = 2 /// let number = 3 /// let message = """ /// If one cookie costs \(price) dollars, \ /// \(number) cookies cost \(price * number) dollars. /// """ /// print(message) /// // Prints "If one cookie costs 2 dollars, 3 cookies cost 6 dollars." @inlinable public mutating func appendInterpolation(_ value: T) where T: TextOutputStreamable, T: CustomStringConvertible { value.write(to: &self) } /// Interpolates the given value's textual representation into the /// string literal being created. /// /// You don't need to call this method directly. It's used by the compiler /// when interpreting string interpolations. Instead, use string /// interpolation to create a new string by including values, literals, /// variables, or expressions enclosed in parentheses, prefixed by a /// backslash (`\(`...`)`). /// /// let price = 2 /// let number = 3 /// let message = "If one cookie costs \(price) dollars, " + /// "\(number) cookies cost \(price * number) dollars." /// print(message) /// // Prints "If one cookie costs 2 dollars, 3 cookies cost 6 dollars." @inlinable public mutating func appendInterpolation(_ value: T) where T: TextOutputStreamable { value.write(to: &self) } /// Interpolates the given value's textual representation into the /// string literal being created. /// /// You don't need to call this method directly. It's used by the compiler /// when interpreting string interpolations. Instead, use string /// interpolation to create a new string by including values, literals, /// variables, or expressions enclosed in parentheses, prefixed by a /// backslash (`\(`...`)`). /// /// let price = 2 /// let number = 3 /// let message = """ /// If one cookie costs \(price) dollars, \ /// \(number) cookies cost \(price * number) dollars. /// """ /// print(message) /// // Prints "If one cookie costs 2 dollars, 3 cookies cost 6 dollars." @inlinable public mutating func appendInterpolation(_ value: T) where T: CustomStringConvertible { value.description.write(to: &self) } /// Interpolates the given value's textual representation into the /// string literal being created. /// /// You don't need to call this method directly. It's used by the compiler /// when interpreting string interpolations. Instead, use string /// interpolation to create a new string by including values, literals, /// variables, or expressions enclosed in parentheses, prefixed by a /// backslash (`\(`...`)`). /// /// let price = 2 /// let number = 3 /// let message = """ /// If one cookie costs \(price) dollars, \ /// \(number) cookies cost \(price * number) dollars. /// """ /// print(message) /// // Prints "If one cookie costs 2 dollars, 3 cookies cost 6 dollars." @inlinable public mutating func appendInterpolation(_ value: T) { #if !$Embedded _print_unlocked(value, &self) #else "(cannot print value in embedded Swift)".write(to: &self) #endif } @_alwaysEmitIntoClient @_unavailableInEmbedded public mutating func appendInterpolation(_ value: Any.Type) { _typeName(value, qualified: false).write(to: &self) } /// Creates a string from this instance, consuming the instance in the /// process. @inlinable internal __consuming func make() -> String { return _storage } } extension DefaultStringInterpolation { /// Interpolates the given optional value's textual representation, or the /// specified default string, into the string literal being created. /// /// You don't need to call this method directly. It's used by the compiler /// when interpreting string interpolations where you provide a `default` /// parameter. For example, the following code implicitly calls this method, /// using the value of the `default` parameter when `value` is `nil`: /// /// var age: Int? = 48 /// print("Your age is \(age, default: "unknown")") /// // Prints: Your age is 48 /// age = nil /// print("Your age is \(age, default: "unknown")") /// // Prints: Your age is unknown /// /// - Parameters: /// - value: The value to include in a string interpolation, if non-`nil`. /// - default: The string to include if `value` is `nil`. @_alwaysEmitIntoClient public mutating func appendInterpolation( _ value: T?, default: @autoclosure () -> some StringProtocol ) where T: TextOutputStreamable, T: CustomStringConvertible { if let value { self.appendInterpolation(value) } else { self.appendInterpolation(`default`()) } } /// Interpolates the given optional value's textual representation, or the /// specified default string, into the string literal being created. /// /// You don't need to call this method directly. It's used by the compiler /// when interpreting string interpolations where you provide a `default` /// parameter. For example, the following code implicitly calls this method, /// using the value of the `default` parameter when `value` is `nil`: /// /// var age: Int? = 48 /// print("Your age is \(age, default: "unknown")") /// // Prints: Your age is 48 /// age = nil /// print("Your age is \(age, default: "unknown")") /// // Prints: Your age is unknown /// /// - Parameters: /// - value: The value to include in a string interpolation, if non-`nil`. /// - default: The string to include if `value` is `nil`. @_alwaysEmitIntoClient public mutating func appendInterpolation( _ value: T?, default: @autoclosure () -> some StringProtocol ) where T: TextOutputStreamable { if let value { self.appendInterpolation(value) } else { self.appendInterpolation(`default`()) } } /// Interpolates the given optional value's textual representation, or the /// specified default string, into the string literal being created. /// /// You don't need to call this method directly. It's used by the compiler /// when interpreting string interpolations where you provide a `default` /// parameter. For example, the following code implicitly calls this method, /// using the value of the `default` parameter when `value` is `nil`: /// /// var age: Int? = 48 /// print("Your age is \(age, default: "unknown")") /// // Prints: Your age is 48 /// age = nil /// print("Your age is \(age, default: "unknown")") /// // Prints: Your age is unknown /// /// - Parameters: /// - value: The value to include in a string interpolation, if non-`nil`. /// - default: The string to include if `value` is `nil`. @_alwaysEmitIntoClient public mutating func appendInterpolation( _ value: T?, default: @autoclosure () -> some StringProtocol ) where T: CustomStringConvertible { if let value { self.appendInterpolation(value) } else { self.appendInterpolation(`default`()) } } /// Interpolates the given optional value's textual representation, or the /// specified default string, into the string literal being created. /// /// You don't need to call this method directly. It's used by the compiler /// when interpreting string interpolations where you provide a `default` /// parameter. For example, the following code implicitly calls this method, /// using the value of the `default` parameter when `value` is `nil`: /// /// var age: Int? = 48 /// print("Your age is \(age, default: "unknown")") /// // Prints: Your age is 48 /// age = nil /// print("Your age is \(age, default: "unknown")") /// // Prints: Your age is unknown /// /// - Parameters: /// - value: The value to include in a string interpolation, if non-`nil`. /// - default: The string to include if `value` is `nil`. @_alwaysEmitIntoClient public mutating func appendInterpolation( _ value: T?, default: @autoclosure () -> some StringProtocol ) { if let value { self.appendInterpolation(value) } else { self.appendInterpolation(`default`()) } } } extension DefaultStringInterpolation: CustomStringConvertible { @inlinable public var description: String { return _storage } } extension DefaultStringInterpolation: TextOutputStream { @inlinable public mutating func write(_ string: String) { _storage.append(string) } public mutating func _writeASCII(_ buffer: UnsafeBufferPointer) { unsafe _storage._guts.append(_StringGuts(buffer, isASCII: true)) } } // While not strictly necessary, declaring these is faster than using the // default implementation. extension String { /// Creates a new instance from an interpolated string literal. /// /// You don't need to call this initializer directly. It's used by the /// compiler when you create a string using string interpolation. Instead, use /// string interpolation to create a new string by including values, literals, /// variables, or expressions enclosed in parentheses, prefixed by a /// backslash (`\(`...`)`). /// /// let price = 2 /// let number = 3 /// let message = """ /// If one cookie costs \(price) dollars, \ /// \(number) cookies cost \(price * number) dollars. /// """ /// print(message) /// // Prints "If one cookie costs 2 dollars, 3 cookies cost 6 dollars." @inlinable @_effects(readonly) public init(stringInterpolation: DefaultStringInterpolation) { self = stringInterpolation.make() } } extension Substring { /// Creates a new instance from an interpolated string literal. /// /// You don't need to call this initializer directly. It's used by the /// compiler when you create a string using string interpolation. Instead, use /// string interpolation to create a new string by including values, literals, /// variables, or expressions enclosed in parentheses, prefixed by a /// backslash (`\(`...`)`). /// /// let price = 2 /// let number = 3 /// let message = """ /// If one cookie costs \(price) dollars, \ /// \(number) cookies cost \(price * number) dollars. /// """ /// print(message) /// // Prints "If one cookie costs 2 dollars, 3 cookies cost 6 dollars." @inlinable @_effects(readonly) public init(stringInterpolation: DefaultStringInterpolation) { self.init(stringInterpolation.make()) } }