blob: 6ab0d1ee5859c0d9f270fa87a2153690e3b4aec2 [file] [log] [blame]
// RUN: %target-run-simple-swift | %FileCheck %s
// REQUIRES: executable_test
// Text Formatting Prototype
//
// This file demonstrates the concepts proposed in TextFormatting.rst
//
// We may not want the following protocol exactly as-is; it overlaps somewhat
// with CustomStringConvertible and CustomDebugStringConvertible, and it's not
// clear whether a protocol is even needed.
/// A type that supports format() and debugFormat().
protocol CustomFormatted : CustomStringConvertible, CustomDebugStringConvertible {
associatedtype DebugRepresentation : TextOutputStreamable = String
associatedtype PrintRepresentation : TextOutputStreamable = DebugRepresentation
/// Produce a textual representation for the REPL and
/// Debugger.
///
/// Because String is a TextOutputStreamable, your implementation of
/// debugRepresentation can just return a String. If you want to write
/// directly to the TextOutputStream for efficiency reasons,
/// (e.g. if your representation is huge), you can return a custom
/// DebugRepresentation type.
///
/// NOTE: producing a representation that can be consumed by the
/// REPL to produce an equivalent object is strongly encouraged
/// where possible! For example, String.debugFormat() produces a
/// representation containing quotes, where special characters are
/// escaped, etc. A struct Point { var x, y: Int } might be
/// represented as "Point(x: 3, y: 5)".
func debugFormat() -> DebugRepresentation
/// produce a "pretty" textual representation that can be
/// distinct from the debug format. For example,
/// String.printRepresentation returns the string itself, without quoting.
///
/// In general you can return a String here, but if you need more
/// control, we strongly recommend returning a custom Representation
/// type, e.g. a nested struct of your type. If you're lazy, you
/// can conform to TextOutputStreamable directly and just implement its
/// write() func.
func format() -> PrintRepresentation
}
extension CustomFormatted where PrintRepresentation == DebugRepresentation {
func format() -> DebugRepresentation { return debugFormat() }
}
/// CustomFormatted's conformance to CustomStringConvertible and
/// CustomDebugStringConvertible
extension CustomFormatted {
public var description: String {
return _description
}
public var debugDescription: String {
return _debugDescription
}
//===--- Using [debug]description directly might pick up the stdlib -----===//
//===--- versions and skew test results, so use these for testing -------===//
var _description: String {
var result = ""
x.format().write(to: &result)
return result
}
var _debugDescription: String {
var result = ""
x.debugFormat().write(to: &result)
return result
}
}
extension String : CustomFormatted {
// Streamer for the debug representation of String. When an
// EscapedStringFormat is written to a TextOutputStream, it adds
// surrounding quotes and escapes special characters.
struct EscapedFormat : TextOutputStreamable {
init(_ s: String) {
self._value = s
}
func write<Target: TextOutputStream>(to target: inout Target) {
target.write("\"")
for c in _value.unicodeScalars {
target.write(c.escaped(asASCII: true))
}
target.write("\"")
}
var _value: String
}
func debugFormat() -> EscapedFormat {
return EscapedFormat(self)
}
func format() -> String {
return self
}
}
//===--- Updated Integer APIs ---------------------------------------------===//
// These will be redundant once the new integer protocols are merged to trunk
// but in the meantime they give us a decent protocol and explicit conversions
// among integers. You can skip this section if only interested in formatting
//===----------------------------------------------------------------------===//
public typealias Integer = Swift.FixedWidthInteger
extension Integer {
/// Creates an instance with the same value as `i`
///
/// - Precondition: `i`'s value is representable by `Self`.
init <N: Integer>(_ i: N) {
// Implementation based on Egyptian Multiplication of i by 1 allows us to
// avoid any mixed-type numeric operations, which the current standard
// library doesn't support. Note: we also don't have a protocol that
// provides shift operators, so we use / 2 here.
var n = i
self = 0
if n == 0 { return }
if n < 0 {
self -= 1 as Self
}
else {
self = 1
}
// compute:
// abs(self) = the greatest power of 2 that divides i.
// n = i / self
while (n & 1) == 0 {
self += self
n = n / 2
}
n /= 2 // consume the lowest 1-bit in i
if n == 0 { return }
// Accumulate the rest of the 1 bits in i.
// invariants:
// abs(a) is a power of 2
// n = i / a
var a = self + self
while true {
if n & 1 != 0 {
self += a
if n / 2 == 0 { return }
}
n /= 2
a += a
}
}
}
//===--- End Updated Integer APIs -----------------------------------------===//
/// The way all Integer types are formatted.
extension CustomFormatted where Self : Integer {
func debugFormat() -> IntegerFormat<Self> {
return format(radix: 10)
}
func format(
radix: Int = 10, fill: String = " ", width: Int = 0
) -> IntegerFormat<Self> {
return IntegerFormat(value: self, radix: radix, fill: fill, width: width)
}
}
/// A textual representation for integers
public struct IntegerFormat<T: Integer> : TextOutputStreamable {
public init(value: T, radix: Int, fill: String, width: Int) {
self.value = value
self.radix = T(radix)
self.fill = fill
self.width = width
}
private var value: T
private var radix: T, fill: String, width: Int
private func _writePositive<S: TextOutputStream>(
_ value: T, _ stream: inout S
) -> Int {
if value == 0 {
return 0
}
let rest: T = value / radix
let nDigits = _writePositive(rest, &stream)
let digit = UInt32(value % radix)
let baseCharOrd : UInt32 = digit <= 9
? UnicodeScalar("0").value
: UnicodeScalar("A").value - 10
stream.write(String(UnicodeScalar(baseCharOrd + digit)!))
return nDigits + 1
}
public func write<Target: TextOutputStream>(to target: inout Target) {
var width = 0
var result = ""
if value == 0 {
result = "0"
width += 1
}
else {
let absVal = value < 0 ? 0 - value : value
if (value < 0) {
target.write("-")
width += 1
}
width += _writePositive(absVal, &result)
}
while width < self.width {
width += 1
target.write(self.fill)
}
target.write(result)
}
}
extension Int : CustomFormatted {}
extension UInt : CustomFormatted {}
//===--- Adapter that encloses text in vertical bars, for testing ---------===//
/// A textual representation that encloses some base `TextOutputStreamable` in
/// vertical bars, making padding spaces more apparent.
///
/// - Note: This is just for the benefit of the tests
struct Delimited<T: TextOutputStreamable> : TextOutputStreamable {
var base: T
func write<S: TextOutputStream>(to output: inout S) {
"|".write(to: &output)
base.write(to: &output)
"|".write(to: &output)
}
}
extension TextOutputStreamable {
/// A textual representation that encloses the plain textual representation of
/// `self` in vertical bars, making padding spaces more apparent.
///
/// - Note: This is just for the benefit of the tests
var delimited : Delimited<Self> {
return Delimited(base: self)
}
}
//===--- Tests ------------------------------------------------------------===//
var x = "fubar\n\tbaz"
print(x._description)
// CHECK: fubar
// CHECK-NEXT: baz
print(x._debugDescription)
// CHECK-NEXT: "fubar\n\tbaz"
print(424242.format(radix:16, width:8).delimited)
// CHECK-NEXT: | 67932|
var zero = "0"
print((-434343).format(fill:zero, width:8).delimited)
// CHECK-NEXT: |-0434343|
print((-42).format(radix:13, width:8).delimited)
// CHECK-NEXT: |- 33|
print(0x1EADBEEF.format(radix:16).delimited)
// CHECK-NEXT: 1EADBEEF
print((0xDEADBEEF as UInt).format(radix:16).delimited)
// CHECK-NEXT: DEADBEEF