blob: 0df1cf32cb9fc80560dba8d295422eea66a020c2 [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
// FIXME: Workaround for <rdar://problem/14011860> SubTLF: Default
// implementations in protocols.
infix operator ~> { precedence 255 }
/// \brief A thing into which we can stream text
protocol XOutputStream {
mutating
func append(_ text: String)
}
/// \brief Strings are XOutputStreams
extension String: XOutputStream {
mutating
func append(_ text: String) {
self += text
}
}
/// \brief A thing that can be written to an XOutputStream
protocol XStreamable {
func writeTo<Target: XOutputStream>(_ target: inout Target)
}
/// \brief A thing that can be printed in the REPL and the Debugger
///
/// Everything compiler-magically conforms to this protocol. To
/// change the debug representation for a type, you don't need to
/// declare conformance: simply give the type a debugFormat().
protocol XDebugPrintable {
associatedtype DebugRepresentation : XStreamable // = String
/// \brief Produce a textual representation for the REPL and
/// Debugger.
///
/// Because String is a XStreamable, your implementation of
/// debugRepresentation can just return a String. If you want to write
/// directly to the XOutputStream 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
}
/// \brief Strings are XStreamable
extension String : XStreamable {
func writeTo<Target: XOutputStream>(_ target: inout Target) {
target.append(self)
}
}
// FIXME: Should be a method of XDebugPrintable once
// <rdar://problem/14692224> (Default Implementations in Protocols) is
// handled
func toDebugString <T:XDebugPrintable> (_ x: T) -> String {
var result = ""
x.debugFormat().writeTo(&result)
return result
}
// FIXME: The following should be a method of XPrintable once
// <rdar://problem/14692224> (Default Implementations in Protocols) is
// handled
struct __PrintedFormat {}
func format() -> __PrintedFormat {
return __PrintedFormat()
}
func ~> <T:XDebugPrintable> (x: T, _: __PrintedFormat) -> T.DebugRepresentation {
return x.debugFormat()
}
/// \brief A thing that can be xprint()ed and toString()ed.
///
/// Conformance to XPrintable is explicit, but if you want to use the
/// debugFormat() results for your type's format(), all you need
/// to do is declare conformance to XPrintable, and there's nothing to
/// implement.
protocol XPrintable: XDebugPrintable {
associatedtype PrintRepresentation: XStreamable = DebugRepresentation
/// \brief 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 XStreamable directly and just implement its
/// write() func.
func ~> (x: Self, _: __PrintedFormat) -> PrintRepresentation
}
// FIXME: The following should be a method of XPrintable once
// <rdar://problem/14692224> (Default Implementations in Protocols) is
// handled
/// \brief Simply convert to String
///
/// Don't reimplement this: the default implementation always works.
/// If you must reimplement toString(), make sure its results are
/// consistent with those of format() (i.e. you shouldn't
/// change the behavior).
func toString<T: XPrintable>(_ x: T) -> String {
var result = ""
(x~>format()).writeTo(&result)
return result
}
// \brief Streamer for the debug representation of String. When an
// EscapedStringFormat is written to a XOutputStream, it adds
// surrounding quotes and escapes special characters.
struct EscapedStringFormat : XStreamable {
init(_ s: String) {
self._value = s
}
func writeTo<Target: XOutputStream>(_ target: inout Target) {
target.append("\"")
for c in _value.unicodeScalars {
target.append(c.escaped(asASCII: true))
}
target.append("\"")
}
var _value: String
}
// FIXME: In theory, this shouldn't be needed
extension String: XDebugPrintable {}
extension String : XPrintable {
func debugFormat() -> EscapedStringFormat {
return EscapedStringFormat(self)
}
func format() -> String {
return self
}
}
/// \brief An integral type that can be printed
protocol XPrintableInteger : ExpressibleByIntegerLiteral, Comparable, SignedNumber, XPrintable {
func %(lhs: Self, rhs: Self) -> Self
func /(lhs: Self, rhs: Self) -> Self
// FIXME: Stand-in for constructor pending <rdar://problem/13695680>
// (Constructor requirements in protocols)
static func fromInt(_ x: Int) -> Self
func toInt() -> Int
}
extension Int : XDebugPrintable {
func debugFormat() -> String { return String(self) }
}
extension Int : XPrintableInteger {
static func fromInt(_ x: Int) -> Int { return x }
func toInt() -> Int { return self }
func getValue() -> Int {
return self
}
}
struct _formatArgs {
var radix: Int, fill: String, width: Int
}
func format(radix radix: Int = 10, fill: String = " ", width: Int = 0) -> _formatArgs {
return _formatArgs(radix: radix, fill: fill, width: width)
}
// FIXME: this function was a member of RadixFormat, but
// <rdar://problem/15525229> (SIL verification failed: operand of
// 'apply' doesn't match function input type) changed all that.
func _writePositive<T:XPrintableInteger, S: XOutputStream>(
_ value: T, _ stream: inout S, _ args: _formatArgs) -> Int
{
if value == 0 {
return 0
}
var radix: T = T.fromInt(args.radix)
var rest: T = value / radix
var nDigits = _writePositive(rest, &stream, args)
var digit = UInt32((value % radix).toInt())
var baseCharOrd : UInt32 = digit <= 9 ? UnicodeScalar("0").value
: UnicodeScalar("A").value - 10
stream.append(String(UnicodeScalar(baseCharOrd + digit)!))
return nDigits + 1
}
// FIXME: this function was a member of RadixFormat, but
// <rdar://problem/15525229> (SIL verification failed: operand of
// 'apply' doesn't match function input type) changed all that.
func _writeSigned<T:XPrintableInteger, S: XOutputStream>(
_ value: T, _ target: inout S, _ args: _formatArgs
) {
var width = 0
var result = ""
if value == 0 {
result = "0"
width += 1
}
else {
var absVal = abs(value)
if (value < 0) {
target.append("-")
width += 1
}
width += _writePositive(absVal, &result, args)
}
while width < args.width {
width += 1
target.append(args.fill)
}
target.append(result)
}
struct RadixFormat<T: XPrintableInteger> : XStreamable {
init(value: T, args: _formatArgs) {
self.value = value
self.args = args
}
func writeTo<S: XOutputStream>(_ target: inout S) {
_writeSigned(value, &target, args)
}
typealias DebugRepresentation = String
func debugFormat() -> String {
return "RadixFormat(" + toDebugString(value) + ", " + toDebugString(args.radix) + ")"
}
var value: T
var args: _formatArgs
}
func ~> <T:XPrintableInteger> (x: T, _: __PrintedFormat) -> RadixFormat<T> {
return RadixFormat(value: x, args: format())
}
func ~> <T:XPrintableInteger> (x: T, args: _formatArgs) -> RadixFormat<T> {
return RadixFormat(value: x, args: args)
}
// ==========
//
// xprint and xprintln
//
struct StdoutStream : XOutputStream {
func append(_ text: String) { Swift.print(text, terminator: "") }
// debugging only
func dump() -> String {
return "<StdoutStream>"
}
}
func xprint<Target: XOutputStream, T: XStreamable>(_ target: inout Target, _ x: T) {
x.writeTo(&target)
}
func xprint<Target: XOutputStream, T: XPrintable>(_ target: inout Target, _ x: T) {
xprint(&target, x~>format())
}
func xprint<T: XPrintable>(_ x: T) {
var target = StdoutStream()
xprint(&target, x)
}
func xprint<T: XStreamable>(_ x: T) {
var target = StdoutStream()
xprint(&target, x)
}
func xprintln<Target: XOutputStream, T: XPrintable>(_ target: inout Target, _ x: T) {
xprint(&target, x)
target.append("\n")
}
func xprintln<Target: XOutputStream, T: XStreamable>(_ target: inout Target, _ x: T) {
xprint(&target, x)
target.append("\n")
}
func xprintln<T: XPrintable>(_ x: T) {
var target = StdoutStream()
xprintln(&target, x)
}
func xprintln<T: XStreamable>(_ x: T) {
var target = StdoutStream()
xprintln(&target, x)
}
func xprintln(_ x: String) {
var target = StdoutStream()
x.writeTo(&target)
"\n".writeTo(&target)
}
extension String {
init <T: XStreamable>(_ x: T) {
self = ""
xprint(&self, x)
}
}
func toPrettyString<T: XStreamable>(_ x: T) -> String {
var result = "|"
xprint(&result, x)
result += "|"
return result
}
// ===========
var x = "fubar\n\tbaz"
xprintln(x)
// CHECK: fubar
// CHECK-NEXT: baz
xprintln(toDebugString(x))
// CHECK-NEXT: "fubar\n\tbaz"
xprintln(toPrettyString(424242~>format(radix:16, width:8)))
// CHECK-NEXT: | 67932|
var zero = "0"
xprintln(toPrettyString(-434343~>format(fill:zero, width:8)))
// CHECK-NEXT: |-0434343|
xprintln(toPrettyString(-42~>format(radix:13, width:8)))
// CHECK-NEXT: |- 33|
xprintln(0x1EADBEEF~>format(radix:16))
// CHECK-NEXT: 1EADBEEF
// FIXME: rdar://16168414 this doesn't work in 32-bit
// xprintln(0xDEADBEEF~>format(radix:16))
// CHECK-NEXT-not: DEADBEEF