1 Mixins define: #StructuredPrinterMixin &parents: {LineNumberedWriteStreamMixin}
2 &slots: {#level -> 0. #columnsPerLine -> 80}.
4 StructuredPrinterMixin traits define: #indentString -> ' '.
5 "The String to insert for each indentation level."
7 o@(StructuredPrinterMixin traits) newLineAndIndent
8 "Append a newLine and indent to the current level."
11 o level timesRepeat: [o ; o indentString].
15 o@(StructuredPrinterMixin traits) newLine
16 "Obsolete; change senders to use newLineAndIndent."
19 o@(StructuredPrinterMixin traits) newColumn
20 "Increment the current column, using wrap-around semantics based on the
21 number of columnsPerLine."
23 o currentColumn > o columnsPerLine
24 ifTrue: [o newLineAndIndent]
25 ifFalse: [o nextPut: $\s]
28 o@(StructuredPrinterMixin traits) indent
29 "Increment the indentation level."
34 o@(StructuredPrinterMixin traits) unindent
35 "Decrement the indentation level."
40 o@(StructuredPrinterMixin traits) indentedDo: block
43 [block do] ensure: [o unindent].
46 o@(StructuredPrinterMixin traits) print: str paddingUntil: length
47 "Print the given string and make sure the cursor is at least at the given column."
49 padding ::= length - str size max: 0.
51 o next: padding put: $\s.
55 Mixins define: #PrettyPrinterMixin &parents: {StructuredPrinterMixin} &slots: {
57 #numberPrecision -> 6.
58 #floatMinimum -> 1e-6.
60 #collectionLimit -> 32.
66 o@(PrettyPrinterMixin traits) newElement
70 ifFalse: [o newLineAndIndent]
73 Stream traits define: #PrettyPrinter &parents: {PrettyPrinterMixin. Stream WrapperStream}.
75 s@(WriteStream traits) prettyPrinter
76 [s PrettyPrinter newOn: s].
78 c@(Collection traits) prettyPrinter
79 "Answer a new PrettyPrinter on the argument."
80 [c writer prettyPrinter].
82 x@(Root traits) prettyPrintOn: s &depth: depth
83 [x printOn: s writer prettyPrinter &depth: depth].
85 x@(Root traits) prettyPrinted &depth: depth
86 [(x prettyPrintOn: '') contents &depth: depth].
88 "Ensure the Console writer and DebugConsole writer are PrettyPrinter objects."
89 "Only do this once during bootstrap"
90 (Console writeStream is: Stream PrettyPrinter)
91 ifFalse: [Console atSlotNamed: #writeStream put: (Stream PrettyPrinter newOn: Console writer)].
92 (DebugConsole writeStream is: Stream PrettyPrinter)
93 ifFalse: [DebugConsole atSlotNamed: #writeStream put: (Stream PrettyPrinter newOn: DebugConsole writer)].
95 x@(Root traits) printString &radix: radix &precision: prec &forceSign: forceSign &depth: depth
96 "Utility for pretty-printing to a String."
98 [printer ::= '' prettyPrinter.
99 x printOn: printer &radix: radix &precision: prec &forceSign: forceSign &depth: depth.
100 printer contents] on: Error do: [| :c | ^ '<Printing failed>']
103 x@(Root traits) printOn: out &radix: radix &precision: prec &forceSign: forceSign &depth: depth
104 "Print the object on the Console. Always answers the object back."
106 x printOn: out writer &radix: radix &precision: prec &forceSign: forceSign &depth: depth
109 x@(Root traits) print &radix: radix &precision: prec &forceSign: forceSign &depth: depth
110 "Print the object on the Console. Always answers the object back."
112 x printOn: Console writer &radix: radix &precision: prec &forceSign: forceSign &depth: depth.
116 "Default print methods."
118 x printOn: o@(PrettyPrinterMixin traits)
120 o ; '#("Unprintable")'
123 x@(Root traits) printOn: o@(PrettyPrinterMixin traits) &depth: depth
124 "Handle the (non-dispatchable) NoRole case, then print whatever suitable
125 defined name along in braces and followed by the slot names and values,
130 (depth `defaultsTo: 3) isZero ifTrue:
133 (doPrintName ::= #printName isFoundOn: {x}) ifTrue:
135 x slotNames isEmpty ifFalse:
138 o level >= o nestingLimit
144 o preferDense ifFalse:
145 [o newLineAndIndent].
146 (x slotNames copyWithout: #traitsWindow) do:
148 o ; (each as: String) ; ': '.
149 (x atSlotNamed: each) printOn: o &depth: depth - 1]
153 (index += 1) >= o collectionLimit ifTrue:
154 [^ (o ; '...)')]]]]].
158 ns@(Namespace traits) printOn: o@(PrettyPrinterMixin traits) &depth: depth
159 "Print out only the slot names, since these tend to be really large and have
160 unprintable prototypes in them."
162 (depth `defaultsTo: 3) isZero ifTrue:
165 (doPrintName ::= #name isFoundOn: {ns}) ifTrue:
167 ns slotNames isEmpty ifFalse:
170 o level >= o nestingLimit
177 do: [| :each | o ; (each as: String)]
181 (index += 1) >= o collectionLimit ifTrue:
182 [^ (o ; '...)')]]]]].
186 s@(Symbol traits) printOn: o@(PrettyPrinterMixin traits)
187 "Print the Symbol readably, so that highlighting and evaluating answers the
192 [s isEmpty] -> [o ; '\'\''].
193 [s allSatisfy: [| :c | c isAlphanumeric \/ [':&_' includes: c]]] -> [o ; s name]
194 ) otherwise: [s name printOn: o]
197 n@(Integer traits) printOn: o@(PrettyPrinterMixin traits) &radix: radix &forceSign: forceSign &groupSeparator: groupSeparator &groupSize: groupSize
198 "Print the Integer in any given basis between 2 and 36 (alphanumeric limit),
199 with sign. This works by collecting the digits and a "
201 n == SmallInteger ifTrue:
202 [^ (o ; n printName)].
203 radix `defaultsTo: o numberRadix.
204 radix < 2 \/ [radix > 36] ifTrue:
209 n negated printOn: o &radix: radix.
212 [forceSign isNotNil /\ [forceSign]
216 digits := '' prettyPrinter.
221 qr := x quoRem: radix.
226 ifTrue: [($A as: Integer) + d - 10]
227 ifFalse: [($0 as: Integer) + d]) as: ASCIIString Character)].
229 ifNil: [digits contents reversed]
230 ifNotNil: [((digits contents splitIntoSize: (groupSize ifNil: [3]))
231 join &separator: groupSeparator reversed) reversed])
234 n@(Integer traits) as: _@(String traits)
239 _@Nil printOn: o@(PrettyPrinterMixin traits)
242 _@True printOn: o@(PrettyPrinterMixin traits)
243 [o ; True printName].
245 _@False printOn: o@(PrettyPrinterMixin traits)
246 [o ; False printName].
248 m@(Method traits) printOn: o@(PrettyPrinterMixin traits)
249 "A simple print-out of brackets and the selector name if there is one."
251 o ; '[' ; (m selector
252 ifNil: ['(arity: ' ; m arity printString ; ')']
253 ifNotNilDo: #name `er).
254 m optionalKeywords do: [| :kw | o ; ' ' ; kw name].
258 m@(Method Converse traits) printOn: o@(PrettyPrinterMixin traits)
259 [m method printOn: o. o ; ' converse'].
261 f@(Float traits) printOn: o@(PrettyPrinterMixin traits)
262 &radix: radix &precision: precision &forceSign: forceSign
263 &groupSeparator: groupSeparator &decimal: decimalSep
264 "Taken from 'Printing Floating-Point Numbers Quickly and Accurately',
265 Robert G. Burger and R. Kent Dybvig, PLDI '96."
266 [| digits significand exponent k b r s mUp mDown d decimal |
267 radix `defaultsTo: o numberRadix.
268 precision `defaultsTo: o numberPrecision.
269 decimalSep `defaultsTo: '.'.
270 significand := f significand.
271 (exponent := f exponent) = (f bias * 2 + 1)
273 [^ (o ; (f isNegative ifTrue: ['-'] ifFalse: ['+'])
274 ; (significand isZero ifTrue: ['Inf'] ifFalse: ['NaN']))].
277 [significand isZero ifTrue:
278 [^ (o ; (f first isZero ifTrue: [''] ifFalse: ['-']) ; '0.0')].
281 [significand := (1 bitShift: f significandSize) bitOr: significand].
283 (exponent := exponent - f bias - f significandSize) isNegative
286 exponent > (1 - f bias - f significandSize)
287 /\ [significand = (1 bitShift: f significandSize)]
289 [r := significand * b * 2.
290 s := (1 bitShift: 1 - exponent) * 2.
293 [r := significand * 2.
294 s := (1 bitShift: exponent negated) * 2.
298 be := 1 bitShift: exponent.
299 significand = (1 bitShift: f significandSize)
301 [r := significand * be * b * 2.
306 [r := significand * be * 2.
310 k := (((significand + 1) ln + (exponent * b ln)) / radix ln) ceiling.
312 ifTrue: [r + mUp > ((radix raisedTo: k) * s)]
313 ifFalse: [(radix raisedTo: k negated) * (r + mUp) > s]]
318 [s *= (radix raisedTo: k)]
321 bk := radix raisedTo: k negated.
325 digits := '' prettyPrinter.
326 [d := r * radix // s.
331 /\ [r >= mDown \/ [r * 2 >= s]]
333 d printOn: digits &radix: radix.
334 r < mDown \/ [r + mUp > s]
336 digits := digits contents.
339 ifFalse: [forceSign isNotNil /\ [forceSign] ifTrue: [o ; '+']].
340 decimal := f abs < o floatMinimum \/ [f abs > o floatMaximum]
343 o next: (decimal min: digits size) putAll: digits.
344 (decimal - digits size max: 0)
345 timesRepeat: [o ; '0'].
350 timesRepeat: [o ; '0']]
353 decimal >= digits size
356 [o next: (precision min: digits size - decimal)
358 startingAt: decimal].
359 k = decimal \/ [decimal <= 0]
362 k - decimal printOn: o &radix: radix].
366 f@(Float traits) as: _@(String traits)
371 x@(Association traits) printOn: o@(PrettyPrinterMixin traits)
378 c@(Collection traits) printContentsOn: o@(PrettyPrinterMixin traits) separatedBy: block &depth: depth
379 "A generic iteration through the elements, with printing of each."
381 (depth `defaultsTo: 3) isZero ifTrue:
387 each printOn: o &depth: depth - 1.
393 c@(Collection traits) printOn: o@(PrettyPrinterMixin traits) &depth: depth
394 "The template for all collection object printing; this is only valid Slate
395 input if the argument is a literal collection, such as an Array."
397 (depth `defaultsTo: 3) isZero ifTrue:
400 (doPrintName ::= #printName isFoundOn: {c}) ifTrue:
405 o level >= o nestingLimit ifTrue:
410 c printContentsOn: o separatedBy:
414 index >= o collectionLimit ifTrue:
415 [o ; '...)'. ^ o]] &depth: depth - 1]].
419 c@(Array traits) printOn: o@(PrettyPrinterMixin traits) &depth: depth
420 "The template for all collection object printing; this is only valid Slate
421 input if the argument is a literal collection, such as an Array."
423 (depth `defaultsTo: 3) isZero ifTrue:
426 (doPrintName ::= (c isSameAs: Array) not) ifTrue:
431 o level >= o nestingLimit ifTrue:
436 c printContentsOn: o separatedBy:
440 index >= o collectionLimit ifTrue:
441 [o ; '...}'. ^ o]] &depth: depth - 1]].
445 c@(Sequence traits) printContentsOn: o@(PrettyPrinterMixin traits) separatedBy: block &depth: depth
446 "A basis for other printing methods."
448 (depth `defaultsTo: 3) isZero ifTrue:
450 c do: [| :each | each printOn: o &depth: depth] separatedBy: [block do].
454 c@(Range traits) printContentsOn: o@(PrettyPrinterMixin traits) separatedBy: block
455 "Print the start to end with ellipses and a fancy step indicator."
458 c end = PositiveInfinity
471 c@(LogicRange traits) printContentsOn: o@(PrettyPrinterMixin traits) separatedBy: block
472 "Print the start to end with ellipses."
478 c@(Dictionary traits) printContentsOn: o@(PrettyPrinterMixin traits) separatedBy: block
479 "Print out the keys and values as Association constructors."
483 c keysAndValuesDo: [| :key :val | tally += 1].
490 < tally ifTrue: [block do]].
494 c@(String Character traits) printOn: o@(PrettyPrinterMixin traits)
495 "Consult the table of escapes, then handle normal printing."
515 ifTrue: [o nextPut: c]
516 ifFalse: "TODO: this is ASCII specific here. See below, too."
518 (c as: Integer) // 16 printOn: o &radix: 16.
519 (c as: Integer) \\ 16 printOn: o &radix: 16]].
523 _@String printOn: o ['' printOn: o].
525 s@(String traits) printOn: o@(PrettyPrinterMixin traits)
526 "Print the String readably, using escapes to preserve the contents."
531 o stringLimit ifNotNil:
532 [index >= o stringLimit ifTrue: [o ; '...\''. ^ o]].
546 ifTrue: [o nextPut: c]
547 ifFalse: "TODO: this is ASCII specific here"
549 (c as: Integer) // 16 printOn: o &radix: 16.
550 (c as: Integer) \\ 16 printOn: o &radix: 16]]].