Newbie Question: How does this work?

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
7 messages Options
Reply | Threaded
Open this post in threaded view
|

Newbie Question: How does this work?

Joseph Alotta
Greetings:

I filed in the below code.  When I ask '%6.2e' what class it is, it replies ByteString.

'%6.2e' class => ByteString

When I send a ByteString the #printf, it somehow knows to look in the FormatString class for this
method.

'%6.2e' printf: 412.343434 => '412.34'

My question is, where is the place in the below code where this gets redirected?  i.e., Where the
printf method gets added to the ByteString path of available methods.


Thanks for your help,

Joe.






Object subclass: #FormatDescriptor
        instanceVariableNames: 'flush width precision'
        classVariableNames: 'Flags Operators'
        poolDictionaries: ''
        category: 'Printf'!

!FormatDescriptor methodsFor: 'rendering'!
applyOperator: object
        self subclassResponsibility! !

!FormatDescriptor methodsFor: 'rendering' stamp: 'hjo 9/18/2011 00:31'!
render: object
        | string |
        string := self applyOperator: object.
        self stringLength ~= 0
                ifTrue: [ string := string copyFrom: 1 to: (self stringLength min: string size) ].
        width == 0
                ifTrue: [ ^ string ].
        ^ (String new: width withAll: self padding)
                copyReplaceFrom: (self startIndexOfCopyReplaceWithStringSize: string size)
                to: (self stopIndexOfCopyReplaceWithStringSize: string size)
                with: string! !

!FormatDescriptor methodsFor: 'rendering' stamp: 'hjo 9/18/2011 00:31'!
startIndexOfCopyReplaceWithStringSize: anInteger

        |start|
        flush == #leftFlush ifTrue: [start := 1].
        flush == #rightFlush ifTrue: [start := width - anInteger + 1].
        ^(start max: 1)
! !

!FormatDescriptor methodsFor: 'rendering' stamp: 'hjo 9/18/2011 00:31'!
stopIndexOfCopyReplaceWithStringSize: anInteger

        | stop |
        flush == #leftFlush ifTrue: [stop := anInteger].
        flush == #rightFlush ifTrue: [stop := width].
        ^stop min: width! !


!FormatDescriptor methodsFor: 'private'!
flush
        ^ flush! !

!FormatDescriptor methodsFor: 'private' stamp: 'mir 6/6/2000 23:58'!
operator: char
        | myself |
        myself := (Smalltalk at: (Operators at: char)) newFrom: self.
        myself setOperator: char.
        ^ myself! !

!FormatDescriptor methodsFor: 'private'!
padding
        ^ Character space! !

!FormatDescriptor methodsFor: 'private'!
precision
        ^ precision! !

!FormatDescriptor methodsFor: 'private'!
precision: anInteger
        precision := anInteger! !

!FormatDescriptor methodsFor: 'private'!
setOperator: char! !

!FormatDescriptor methodsFor: 'private'!
stringLength
        ^ precision isNil ifTrue: [0] ifFalse: [precision]! !

!FormatDescriptor methodsFor: 'private'!
width
        ^ width! !

!FormatDescriptor methodsFor: 'private'!
width: anInteger
        width := anInteger! !


!FormatDescriptor methodsFor: 'initialize-release'!
initialize
        flush := #rightFlush.
        width := 0! !


!FormatDescriptor methodsFor: 'scanning'!
leftFlush
        flush := #leftFlush! !

!FormatDescriptor methodsFor: 'scanning'!
radix
        ^ (NumberFormatDescriptor newFrom: self) radix! !

!FormatDescriptor methodsFor: 'scanning'!
rightFlush
        flush := #rightFlush! !

!FormatDescriptor methodsFor: 'scanning'!
space
        ^ (NumberFormatDescriptor newFrom: self) space! !

!FormatDescriptor methodsFor: 'scanning'!
zero
        ^ (NumberFormatDescriptor newFrom: self) zero! !


!FormatDescriptor methodsFor: 'printing'!
printOn: aStream
        aStream nextPut: $%.
        flush == #leftFlush ifTrue: [aStream nextPut: $-]! !

!FormatDescriptor methodsFor: 'printing'!
printWidthOn: aStream
        width ~= 0 ifTrue: [width printOn: aStream].
        precision isNil ifFalse: [aStream nextPut: $.. precision printOn: aStream]! !

"-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- "!

FormatDescriptor class
        instanceVariableNames: ''!

!FormatDescriptor class methodsFor: 'class initialization' stamp: 'mir 6/7/2000 00:21'!
initialize
        "FormatDescriptor initialize"
        Operators := Dictionary new.
        Operators at: $p put: #PrintStringFormatDescriptor.
        Operators at: $c put: #CharacterFormatDescriptor.
        Operators at: $s put: #StringFormatDescriptor.
        #($d $o $u $x $X)
                do: [:operator | Operators at: operator put: #NumberFormatDescriptor].
        #($e $E $f $g $G)
                do: [:operator | Operators at: operator put: #FloatFormatDescriptor].

        Flags := Dictionary new.
        Flags at: $- put: #leftFlush.
        Flags at: $+ put: #rightFlush.
        Flags at: $  put: #space.
        Flags at: $# put: #radix.
        Flags at: $0 put: #zero.
! !


!FormatDescriptor class methodsFor: 'instance creation'!
new
        ^ super new initialize! !

!FormatDescriptor class methodsFor: 'instance creation'!
newFrom: desc
        | myself |
        myself := self new.
        myself perform: desc flush.
        myself width: desc width.
        myself precision: desc precision.
        ^ myself! !

!FormatDescriptor class methodsFor: 'instance creation'!
scanFrom: stream
        | desc |
        desc := self new.
        [Flags includesKey: stream peek]
                whileTrue: [desc := desc perform: (Flags at: stream next)].
        stream peek isDigit ifTrue: [desc width: (Integer readFrom: stream)].
        stream peek == $. ifTrue: [stream next. desc precision: (Integer readFrom: stream)].
        stream peek == $l ifTrue: [stream next].
        desc := desc operator: stream next.
        ^ desc! !

FormatDescriptor subclass: #CharacterFormatDescriptor
        instanceVariableNames: ''
        classVariableNames: ''
        poolDictionaries: ''
        category: 'Printf'!

!CharacterFormatDescriptor methodsFor: 'rendering'!
applyOperator: object
        ^ String with: object asCharacter! !


!CharacterFormatDescriptor methodsFor: 'printing'!
printOn: aStream
        super printOn: aStream.
        self printWidthOn: aStream.
        aStream nextPut: $c! !

Object subclass: #FormatString
        instanceVariableNames: 'format string composedString'
        classVariableNames: ''
        poolDictionaries: ''
        category: 'Printf'!
!FormatString commentStamp: 'mir 6/7/2000 00:14' prior: 0!
Format description
        syntax: %{flags}{width}{precision}{long}<operator>
       
        flags
                - left flush
                + right flush
                space non-negative number are preceeded by a blank
                # display integer with a radix indicator (0=octal, 0x=hex, float have .)
                0 0 is used as left padding character for numbers
        width minimum field width (rest is padded)
        .precision maximum field width or trailing digits
        long ignored
        operator
                c display object as character
                d display as integer
                e,E float in scientific notation
                f display as float
                g,G display as f or e,E using least amount of space
                o display as octal value
                s display as string
                u display as unsigned
                x,X display as hex value
        !


!FormatString methodsFor: 'printf'!
<< object
        "Render object according to next format descriptor in format.
        Append it to string"
        "Format description
        syntax: %{flags}{width}{precision}{long}<operator>
       
        flags
                - left flush
                + right flush
                space non-negative number are preceeded by a blank
                # display integer with a radix indicator (0=octal, 0x=hex, float have .)
                0 0 is used as left padding character for numbers
        width minimum field width (rest is padded)
        .precision maximum field width or trailing digits
        long ignored
        operator
                c display object as character
                d display as integer
                e,E float in scientific notation
                f display as float
                g,G display as f or e,E using least amount of space
                o display as octal value
                s display as string
                u display as unsigned
                x,X display as hex value
        "

        composedString nextPutAll: string next.
        composedString nextPutAll: (format next render: object).
        format atEnd
                ifTrue:
                        [format reset.
                        composedString nextPutAll: string next.
                        string reset].
        ^composedString contents! !

!FormatString methodsFor: 'printf' stamp: 'hjo 9/17/2011 23:59'!
printf: arguments

        "inst var string holds all text contained in the formatstring. %f blabla %d"
        "inst var format is a stream of FormatDescriptors"

        self reset.
  arguments asArgumentArrayForFormatString do:
                [:object |
                "put any text from the formatstring into composedstring"
                composedString nextPutAll: string next.
                 "get next FormatDescriptor from format and render object as specified"
                format atEnd ifFalse: [composedString nextPutAll: (format next render: object)]].
        "any remainder is string, if so append to composedString"
        string atEnd ifFalse: [composedString nextPutAll: string next].
         ^self stringWithReset.! !

!FormatString methodsFor: 'printf'!
string
        ^composedString contents! !

!FormatString methodsFor: 'printf' stamp: 'hjo 9/17/2011 23:59'!
stringWithReset

        | result |
        result := self string.
        self reset.
        ^result! !


!FormatString methodsFor: 'initialize-release' stamp: 'hjo 9/18/2011 00:05'!
collectFormatDescriptorsAndStrings: formatStream

        | done |
        format := ReadWriteStream on: (Array new: 10).
        string := ReadWriteStream on: (Array new: 10).
        done := false.
        [ done ]
                whileFalse: [
                        "copy actual formatstrings to format"
                        string nextPut: (self scanStringFrom: formatStream).
                        (done := formatStream atEnd)
                                ifFalse: [
                                        "copy any nonformating text to string"
                                        format nextPut: (FormatDescriptor scanFrom: formatStream) ] ].
        self reset! !

!FormatString methodsFor: 'initialize-release' stamp: 'hjo 9/18/2011 00:05'!
setFormat: aString
        | formatStream |
        "copy actual formatstrings to format"
        "copy any nonformating text to string"
        composedString := (String new: 20) writeStream.
        formatStream := ((aString copyReplaceAll: '\n' with: (String with: Character cr))
                copyReplaceAll: '\t'
                with: (String with: Character tab)) readStream.
        self collectFormatDescriptorsAndStrings: formatStream! !


!FormatString methodsFor: 'private'!
reset
        format reset.
        string reset.
        composedString reset! !

!FormatString methodsFor: 'private' stamp: 'hjo 9/18/2011 00:00'!
scanStringFrom: aStream
        | newString |
        newString := (String new: 40) writeStream.
        [aStream atEnd]
                whileFalse:
                        [| next |
                        next := aStream next.
                        next == $% ifTrue: [^newString contents].
                        next == $\
                                ifTrue:
                                        [next := aStream next.
                                        next == $n ifTrue: [next := Character cr].
                                        next == $t ifTrue: [next := Character tab]].
                        newString nextPut: next].
        ^newString contents! !

"-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- "!

FormatString class
        instanceVariableNames: ''!

!FormatString class methodsFor: 'examples' stamp: 'mir 6/7/2000 00:12'!
examples
        self inform: ('Here is a string "%s".' printf: 'hello world').
        self inform: ('Here is a string "%s" and another shortened "%-14.7s".'
                printf: #('hello world' 'hello world')).

        self inform: ('Here is a number "%d".' printf: 42).
        self inform: ('Here is a string "%07.7d".' printf: 42).

        self inform: ('Here is a number "%e".' printf: 42.0).
        self inform: ('Here is a float "%e" and an integer "%d".' printf: #(42.0 42)).
        self inform: ('Here is a string "%013.5e".' printf: 42.1234567).

        self inform: ('Here is a %s string "%s" and the same shortened "%-14.7s" with left flush.\nThe new line has a number "%e" and a 0-padded limited precision one "%013.5e".'
                printf: ((Array with: 'long' with: 'hello world' with: 'hello world' with: 42.0) copyWith: 42.1234567)).! !

TestCase subclass: #FormatStringTest
        instanceVariableNames: ''
        classVariableNames: ''
        poolDictionaries: ''
        category: 'Printf'!

!FormatStringTest methodsFor: 'as yet unclassified' stamp: 'hjo 9/17/2011 23:41'!
testManyArguments

        self assert: 'Here is a long string "hello world" and the same shortened "hello w       " with left flush.
The new line has a number "42.0" and a 0-padded limited precision one "0000042.12345".' equals: ('Here is a %s string "%s" and the same shortened "%-14.7s" with left flush.\nThe new line has a number "%e" and a 0-padded limited precision one "%013.5e".'
                printf: ((Array with: 'long' with: 'hello world' with: 'hello world' with: 42.0) copyWith: 42.1234567)).! !

!FormatStringTest methodsFor: 'as yet unclassified' stamp: 'hjo 9/17/2011 23:41'!
testOneExponent

        self assert: 'Here is a number "42.0".' equals: ('Here is a number "%e".' printf: 42.0).! !

!FormatStringTest methodsFor: 'as yet unclassified' stamp: 'hjo 9/17/2011 23:41'!
testOneExponentAndOneInteger

        self assert: 'Here is a float "42.0" and an integer "42".' equals: ('Here is a float "%e" and an integer "%d".' printf: #(42.0 42)).! !

!FormatStringTest methodsFor: 'as yet unclassified' stamp: 'hjo 9/17/2011 23:41'!
testOneExponentWithLength

        self assert: 'Here is a string "0000042.12345".' equals: ('Here is a string "%013.5e".' printf: 42.1234567).! !

!FormatStringTest methodsFor: 'as yet unclassified' stamp: 'hjo 9/17/2011 23:40'!
testOneInteger

        self assert: 'Here is a number "42".' equals: ('Here is a number "%d".' printf: 42).! !

!FormatStringTest methodsFor: 'as yet unclassified' stamp: 'hjo 9/17/2011 23:40'!
testOneIntegerWithLength

        self assert: 'Here is a string "0000042".' equals: ('Here is a string "%07.7d".' printf: 42).! !

!FormatStringTest methodsFor: 'as yet unclassified' stamp: 'hjo 9/17/2011 23:40'!
testOneString

        self assert: 'Here is a string "hello world".' equals: ('Here is a string "%s".' printf: 'hello world')! !

!FormatStringTest methodsFor: 'as yet unclassified' stamp: 'hjo 9/17/2011 23:40'!
testOneStringWithLength

        self assert:  'Here is a string "hello world" and another shortened "hello w       ".' equals: ('Here is a string "%s" and another shortened "%-14.7s".' printf: #('hello world' 'hello world'))! !

FormatDescriptor subclass: #NumberFormatDescriptor
        instanceVariableNames: 'operator padding radix space'
        classVariableNames: 'Base Radix'
        poolDictionaries: ''
        category: 'Printf'!

!NumberFormatDescriptor methodsFor: 'rendering' stamp: 'mir 6/6/2000 23:56'!
applyOperator: object
        "Character and Number are the only valid classes"

        | number string |
        object isNil ifTrue: [^'-'].
"object isInteger ifFalse: [self halt].
" number := object asInteger.
        string := number printStringBase: self base.
        radix ifTrue: [string := self radixString , string].
        (space and: [operator == $d and: [number < 0]])
                ifTrue: [string := ' ' , string].
        ^ (width ~= 0 and: [string size > self stringLength])
                ifTrue: [String new: width withAll: $*]
                ifFalse: [string]! !


!NumberFormatDescriptor methodsFor: 'private'!
base
        ^ Base at: operator! !

!NumberFormatDescriptor methodsFor: 'private'!
padding
        ^ padding! !

!NumberFormatDescriptor methodsFor: 'private'!
radixString
        ^ Radix at: operator! !

!NumberFormatDescriptor methodsFor: 'private'!
setOperator: char
        operator := char! !

!NumberFormatDescriptor methodsFor: 'private'!
setPadding: paddingChar
        padding := paddingChar! !

!NumberFormatDescriptor methodsFor: 'private'!
stringLength
        ^precision isNil
                ifTrue: [SmallInteger maxVal]
                ifFalse: [precision]! !


!NumberFormatDescriptor methodsFor: 'initialize-release'!
initialize
        super initialize.
        padding := $ .
        radix := false.
        space := false! !


!NumberFormatDescriptor methodsFor: 'printing'!
printOn: aStream
        super printOn: aStream.
        padding == $0 ifTrue: [aStream nextPut: $0].
        radix ifTrue: [aStream nextPut: $#].
        space ifTrue: [aStream nextPut: $ ].
        self printWidthOn: aStream.
        aStream nextPut: operator! !


!NumberFormatDescriptor methodsFor: 'scanning'!
radix
        radix := true! !

!NumberFormatDescriptor methodsFor: 'scanning'!
space
        space := true! !

!NumberFormatDescriptor methodsFor: 'scanning'!
zero
        padding := $0! !

"-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- "!

NumberFormatDescriptor class
        instanceVariableNames: ''!

!NumberFormatDescriptor class methodsFor: 'class initialization'!
initialize
        "NumberFormatDescriptor initialize"
        Base := Dictionary new.
        Base at: $d put: 10.
        Base at: $o put: 8.
        Base at: $u put: 10.
        Base at: $x put: 16.
        Base at: $X put: 16.

        Radix := Dictionary new.
        Radix at: $d put: ''.
        Radix at: $o put: '0'.
        Radix at: $u put: ''.
        Radix at: $x put: '0x'.
        Radix at: $X put: '0X'.! !


!NumberFormatDescriptor class methodsFor: 'instance creation'!
newFrom: desc
        desc class == self ifTrue: [^ desc].
        ^ (super newFrom: desc) setPadding: desc padding! !

NumberFormatDescriptor subclass: #FloatFormatDescriptor
        instanceVariableNames: ''
        classVariableNames: ''
        poolDictionaries: ''
        category: 'Printf'!

!FloatFormatDescriptor methodsFor: 'rendering' stamp: 'hjo 9/18/2011 00:47'!
applyOperator: object
        "Number is the only valid class"

        | string |
        string := self zeroPaddedStringOfBase10ForFloat: object asFloat.
        string := string copyFrom: 1 to: ((string indexOf: $.) + (precision == 0
                                                ifTrue: [-1]
                                                ifFalse: [self precision]) min: string size).
        (space and: [object asFloat >= 0])
                ifTrue: [string := ' ' , string].
        ^(width ~= 0 and: [string size > width])
                ifTrue: [String new: width withAll: $*]
                ifFalse: [string]! !

!FloatFormatDescriptor methodsFor: 'rendering' stamp: 'hjo 9/18/2011 00:45'!
zeroPaddedStringOfBase10ForFloat: aFloat

        | stream |
        stream := String new writeStream.
        aFloat printOn: stream base: 10.
        stream next: self precision-1 put: $0.
        ^stream contents.
! !


!FloatFormatDescriptor methodsFor: 'private' stamp: 'mir 6/7/2000 00:01'!
digits
        ^ width == 0 ifTrue: [7] ifFalse: [width]! !

!FloatFormatDescriptor methodsFor: 'private'!
precision
        ^ precision isNil ifTrue: [1] ifFalse: [precision]! !

!FloatFormatDescriptor methodsFor: 'private'!
stringLength
        ^ width! !

FormatDescriptor subclass: #StringFormatDescriptor
        instanceVariableNames: ''
        classVariableNames: ''
        poolDictionaries: ''
        category: 'Printf'!

!StringFormatDescriptor methodsFor: 'rendering'!
applyOperator: object
        ^ object! !


!StringFormatDescriptor methodsFor: 'printing'!
printOn: aStream
        super printOn: aStream.
        self printWidthOn: aStream.
        aStream nextPut: $s! !

StringFormatDescriptor subclass: #PrintStringFormatDescriptor
        instanceVariableNames: ''
        classVariableNames: ''
        poolDictionaries: ''
        category: 'Printf'!

!PrintStringFormatDescriptor methodsFor: 'rendering'!
applyOperator: object
        ^ object printLeanString! !


!PrintStringFormatDescriptor methodsFor: 'printing'!
printOn: aStream
        aStream nextPut: $%.
        flush == #leftFlush ifTrue: [aStream nextPut: $-].
        self printWidthOn: aStream.
        aStream nextPut: $p! !

FormatDescriptor initialize!
NumberFormatDescriptor initialize!
Reply | Threaded
Open this post in threaded view
|

Re: Newbie Question: How does this work?

Chris Cunnington
On 12-10-08 6:13 PM, Joseph J Alotta wrote:
> '%6.2e' printf: 412.343434
I loaded in the code you added in Squeak 4.3, executed the above, and
got "MessageNotUnderstood: ByteString>>printf:". I then loaded in Printf
from squeaksource.com/Printf and it worked.

Monticello added some extra methods to various classes in the image.
Don't look at FormatString>>printf: Look at String>>printf:, which is a
superclass of ByteString. The protocol list has printf with an * before
it - *printf. The Monticello package added some extra methods to the
String class.

Chris

Reply | Threaded
Open this post in threaded view
|

Re: Newbie Question: How does this work?

Darius Clarke
In reply to this post by Joseph Alotta
Hi Joe,

Thank you for asking interesting questions and staying involved. 

One way to find this out for yourself is to run this code to step through what is happening (highlighting the two lines and hitting the PrintIt key combo):
self halt. 
'123%s456' printf: ('s').

You'll find that the class ByteString also now has a #printf: method inherited from String class. So, the ByteString class knows how to convert itself to a FormatString if it sees a #printf: method.
printf: arguments
        ^ self asFormatString printf: arguments

The ' * ' in *printf method category name is a visual clue that a load has added it to the default String class.

- Darius
_______________

On Mon, Oct 8, 2012 at 3:13 PM, Joseph J Alotta <[hidden email]> wrote:
Greetings:

I filed in the below code.  When I ask '%6.2e' what class it is, it replies ByteString.

'%6.2e' class => ByteString

When I send a ByteString the #printf, it somehow knows to look in the FormatString class for this
method.

 

'%6.2e' printf: 412.343434 => '412.34'

My question is, where is the place in the below code where this gets redirected?  i.e., Where the
printf method gets added to the ByteString path of available methods.


Thanks for your help,

Joe.






Object subclass: #FormatDescriptor
        instanceVariableNames: 'flush width precision'
        classVariableNames: 'Flags Operators'
        poolDictionaries: ''
        category: 'Printf'!

!FormatDescriptor methodsFor: 'rendering'!
applyOperator: object
        self subclassResponsibility! !

!FormatDescriptor methodsFor: 'rendering' stamp: 'hjo 9/18/2011 00:31'!
render: object
        | string |
        string := self applyOperator: object.
        self stringLength ~= 0
                ifTrue: [ string := string copyFrom: 1 to: (self stringLength min: string size) ].
        width == 0
                ifTrue: [ ^ string ].
        ^ (String new: width withAll: self padding)
                copyReplaceFrom: (self startIndexOfCopyReplaceWithStringSize: string size)
                to: (self stopIndexOfCopyReplaceWithStringSize: string size)
                with: string! !

!FormatDescriptor methodsFor: 'rendering' stamp: 'hjo 9/18/2011 00:31'!
startIndexOfCopyReplaceWithStringSize: anInteger

        |start|
        flush == #leftFlush ifTrue: [start := 1].
        flush == #rightFlush ifTrue: [start := width - anInteger + 1].
        ^(start max: 1)
! !

!FormatDescriptor methodsFor: 'rendering' stamp: 'hjo 9/18/2011 00:31'!
stopIndexOfCopyReplaceWithStringSize: anInteger

        | stop |
        flush == #leftFlush ifTrue: [stop := anInteger].
        flush == #rightFlush ifTrue: [stop := width].
        ^stop min: width! !


!FormatDescriptor methodsFor: 'private'!
flush
        ^ flush! !

!FormatDescriptor methodsFor: 'private' stamp: 'mir 6/6/2000 23:58'!
operator: char
        | myself |
        myself := (Smalltalk at: (Operators at: char)) newFrom: self.
        myself setOperator: char.
        ^ myself! !

!FormatDescriptor methodsFor: 'private'!
padding
        ^ Character space! !

!FormatDescriptor methodsFor: 'private'!
precision
        ^ precision! !

!FormatDescriptor methodsFor: 'private'!
precision: anInteger
        precision := anInteger! !

!FormatDescriptor methodsFor: 'private'!
setOperator: char! !

!FormatDescriptor methodsFor: 'private'!
stringLength
        ^ precision isNil ifTrue: [0] ifFalse: [precision]! !

!FormatDescriptor methodsFor: 'private'!
width
        ^ width! !

!FormatDescriptor methodsFor: 'private'!
width: anInteger
        width := anInteger! !


!FormatDescriptor methodsFor: 'initialize-release'!
initialize
        flush := #rightFlush.
        width := 0! !


!FormatDescriptor methodsFor: 'scanning'!
leftFlush
        flush := #leftFlush! !

!FormatDescriptor methodsFor: 'scanning'!
radix
        ^ (NumberFormatDescriptor newFrom: self) radix! !

!FormatDescriptor methodsFor: 'scanning'!
rightFlush
        flush := #rightFlush! !

!FormatDescriptor methodsFor: 'scanning'!
space
        ^ (NumberFormatDescriptor newFrom: self) space! !

!FormatDescriptor methodsFor: 'scanning'!
zero
        ^ (NumberFormatDescriptor newFrom: self) zero! !


!FormatDescriptor methodsFor: 'printing'!
printOn: aStream
        aStream nextPut: $%.
        flush == #leftFlush ifTrue: [aStream nextPut: $-]! !

!FormatDescriptor methodsFor: 'printing'!
printWidthOn: aStream
        width ~= 0 ifTrue: [width printOn: aStream].
        precision isNil ifFalse: [aStream nextPut: $.. precision printOn: aStream]! !

"-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- "!

FormatDescriptor class
        instanceVariableNames: ''!

!FormatDescriptor class methodsFor: 'class initialization' stamp: 'mir 6/7/2000 00:21'!
initialize
        "FormatDescriptor initialize"
        Operators := Dictionary new.
        Operators at: $p put: #PrintStringFormatDescriptor.
        Operators at: $c put: #CharacterFormatDescriptor.
        Operators at: $s put: #StringFormatDescriptor.
        #($d $o $u $x $X)
                do: [:operator | Operators at: operator put: #NumberFormatDescriptor].
        #($e $E $f $g $G)
                do: [:operator | Operators at: operator put: #FloatFormatDescriptor].

        Flags := Dictionary new.
        Flags at: $- put: #leftFlush.
        Flags at: $+ put: #rightFlush.
        Flags at: $  put: #space.
        Flags at: $# put: #radix.
        Flags at: $0 put: #zero.
! !


!FormatDescriptor class methodsFor: 'instance creation'!
new
        ^ super new initialize! !

!FormatDescriptor class methodsFor: 'instance creation'!
newFrom: desc
        | myself |
        myself := self new.
        myself perform: desc flush.
        myself width: desc width.
        myself precision: desc precision.
        ^ myself! !

!FormatDescriptor class methodsFor: 'instance creation'!
scanFrom: stream
        | desc |
        desc := self new.
        [Flags includesKey: stream peek]
                whileTrue: [desc := desc perform: (Flags at: stream next)].
        stream peek isDigit ifTrue: [desc width: (Integer readFrom: stream)].
        stream peek == $. ifTrue: [stream next. desc precision: (Integer readFrom: stream)].
        stream peek == $l ifTrue: [stream next].
        desc := desc operator: stream next.
        ^ desc! !

FormatDescriptor subclass: #CharacterFormatDescriptor
        instanceVariableNames: ''
        classVariableNames: ''
        poolDictionaries: ''
        category: 'Printf'!

!CharacterFormatDescriptor methodsFor: 'rendering'!
applyOperator: object
        ^ String with: object asCharacter! !


!CharacterFormatDescriptor methodsFor: 'printing'!
printOn: aStream
        super printOn: aStream.
        self printWidthOn: aStream.
        aStream nextPut: $c! !

Object subclass: #FormatString
        instanceVariableNames: 'format string composedString'
        classVariableNames: ''
        poolDictionaries: ''
        category: 'Printf'!
!FormatString commentStamp: 'mir 6/7/2000 00:14' prior: 0!
Format description
        syntax: %{flags}{width}{precision}{long}<operator>

        flags
                -               left flush
                +               right flush
                space   non-negative number are preceeded by a blank
                #               display integer with a radix indicator (0=octal, 0x=hex, float have .)
                0               0 is used as left padding character for numbers
        width           minimum field width (rest is padded)
        .precision      maximum field width or trailing digits
        long            ignored
        operator
                c               display object as character
                d               display as integer
                e,E             float in scientific notation
                f               display as float
                g,G             display as f or e,E using least amount of space
                o               display as octal value
                s               display as string
                u               display as unsigned
                x,X             display as hex value
        !


!FormatString methodsFor: 'printf'!
<< object
        "Render object according to next format descriptor in format.
        Append it to string"
        "Format description
        syntax: %{flags}{width}{precision}{long}<operator>

        flags
                -               left flush
                +               right flush
                space   non-negative number are preceeded by a blank
                #               display integer with a radix indicator (0=octal, 0x=hex, float have .)
                0               0 is used as left padding character for numbers
        width           minimum field width (rest is padded)
        .precision      maximum field width or trailing digits
        long            ignored
        operator
                c               display object as character
                d               display as integer
                e,E             float in scientific notation
                f               display as float
                g,G             display as f or e,E using least amount of space
                o               display as octal value
                s               display as string
                u               display as unsigned
                x,X             display as hex value
        "

        composedString nextPutAll: string next.
        composedString nextPutAll: (format next render: object).
        format atEnd
                ifTrue:
                        [format reset.
                        composedString nextPutAll: string next.
                        string reset].
        ^composedString contents! !

!FormatString methodsFor: 'printf' stamp: 'hjo 9/17/2011 23:59'!
printf: arguments

        "inst var string holds all text contained in the formatstring. %f blabla %d"
        "inst var format is a stream of FormatDescriptors"

        self reset.
        arguments asArgumentArrayForFormatString do:
                [:object |
                "put any text from the formatstring into composedstring"
                composedString nextPutAll: string next.
                 "get next FormatDescriptor from format and render object as specified"
                format atEnd ifFalse: [composedString nextPutAll: (format next render: object)]].
        "any remainder is string, if so append to composedString"
        string atEnd ifFalse: [composedString nextPutAll: string next].
         ^self stringWithReset.! !

!FormatString methodsFor: 'printf'!
string
        ^composedString contents! !

!FormatString methodsFor: 'printf' stamp: 'hjo 9/17/2011 23:59'!
stringWithReset

        | result |
        result := self string.
        self reset.
        ^result! !


!FormatString methodsFor: 'initialize-release' stamp: 'hjo 9/18/2011 00:05'!
collectFormatDescriptorsAndStrings: formatStream

        | done |
        format := ReadWriteStream on: (Array new: 10).
        string := ReadWriteStream on: (Array new: 10).
        done := false.
        [ done ]
                whileFalse: [
                        "copy actual formatstrings to format"
                        string nextPut: (self scanStringFrom: formatStream).
                        (done := formatStream atEnd)
                                ifFalse: [
                                        "copy any nonformating text to string"
                                        format nextPut: (FormatDescriptor scanFrom: formatStream) ] ].
        self reset! !

!FormatString methodsFor: 'initialize-release' stamp: 'hjo 9/18/2011 00:05'!
setFormat: aString
        | formatStream |
        "copy actual formatstrings to format"
        "copy any nonformating text to string"
        composedString := (String new: 20) writeStream.
        formatStream := ((aString copyReplaceAll: '\n' with: (String with: Character cr))
                copyReplaceAll: '\t'
                with: (String with: Character tab)) readStream.
        self collectFormatDescriptorsAndStrings: formatStream! !


!FormatString methodsFor: 'private'!
reset
        format reset.
        string reset.
        composedString reset! !

!FormatString methodsFor: 'private' stamp: 'hjo 9/18/2011 00:00'!
scanStringFrom: aStream
        | newString |
        newString := (String new: 40) writeStream.
        [aStream atEnd]
                whileFalse:
                        [| next |
                        next := aStream next.
                        next == $% ifTrue: [^newString contents].
                        next == $\
                                ifTrue:
                                        [next := aStream next.
                                        next == $n ifTrue: [next := Character cr].
                                        next == $t ifTrue: [next := Character tab]].
                        newString nextPut: next].
        ^newString contents! !

"-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- "!

FormatString class
        instanceVariableNames: ''!

!FormatString class methodsFor: 'examples' stamp: 'mir 6/7/2000 00:12'!
examples
        self inform: ('Here is a string "%s".' printf: 'hello world').
        self inform: ('Here is a string "%s" and another shortened "%-14.7s".'
                printf: #('hello world' 'hello world')).

        self inform: ('Here is a number "%d".' printf: 42).
        self inform: ('Here is a string "%07.7d".' printf: 42).

        self inform: ('Here is a number "%e".' printf: 42.0).
        self inform: ('Here is a float "%e" and an integer "%d".' printf: #(42.0 42)).
        self inform: ('Here is a string "%013.5e".' printf: 42.1234567).

        self inform: ('Here is a %s string "%s" and the same shortened "%-14.7s" with left flush.\nThe new line has a number "%e" and a 0-padded limited precision one "%013.5e".'
                printf: ((Array with: 'long' with: 'hello world' with: 'hello world' with: 42.0) copyWith: 42.1234567)).! !

TestCase subclass: #FormatStringTest
        instanceVariableNames: ''
        classVariableNames: ''
        poolDictionaries: ''
        category: 'Printf'!

!FormatStringTest methodsFor: 'as yet unclassified' stamp: 'hjo 9/17/2011 23:41'!
testManyArguments

        self assert: 'Here is a long string "hello world" and the same shortened "hello w       " with left flush.
The new line has a number "42.0" and a 0-padded limited precision one "0000042.12345".' equals: ('Here is a %s string "%s" and the same shortened "%-14.7s" with left flush.\nThe new line has a number "%e" and a 0-padded limited precision one "%013.5e".'
                printf: ((Array with: 'long' with: 'hello world' with: 'hello world' with: 42.0) copyWith: 42.1234567)).! !

!FormatStringTest methodsFor: 'as yet unclassified' stamp: 'hjo 9/17/2011 23:41'!
testOneExponent

        self assert: 'Here is a number "42.0".' equals: ('Here is a number "%e".' printf: 42.0).! !

!FormatStringTest methodsFor: 'as yet unclassified' stamp: 'hjo 9/17/2011 23:41'!
testOneExponentAndOneInteger

        self assert: 'Here is a float "42.0" and an integer "42".' equals: ('Here is a float "%e" and an integer "%d".' printf: #(42.0 42)).! !

!FormatStringTest methodsFor: 'as yet unclassified' stamp: 'hjo 9/17/2011 23:41'!
testOneExponentWithLength

        self assert: 'Here is a string "0000042.12345".' equals: ('Here is a string "%013.5e".' printf: 42.1234567).! !

!FormatStringTest methodsFor: 'as yet unclassified' stamp: 'hjo 9/17/2011 23:40'!
testOneInteger

        self assert: 'Here is a number "42".' equals: ('Here is a number "%d".' printf: 42).! !

!FormatStringTest methodsFor: 'as yet unclassified' stamp: 'hjo 9/17/2011 23:40'!
testOneIntegerWithLength

        self assert: 'Here is a string "0000042".' equals: ('Here is a string "%07.7d".' printf: 42).! !

!FormatStringTest methodsFor: 'as yet unclassified' stamp: 'hjo 9/17/2011 23:40'!
testOneString

        self assert: 'Here is a string "hello world".' equals: ('Here is a string "%s".' printf: 'hello world')! !

!FormatStringTest methodsFor: 'as yet unclassified' stamp: 'hjo 9/17/2011 23:40'!
testOneStringWithLength

        self assert:  'Here is a string "hello world" and another shortened "hello w       ".' equals: ('Here is a string "%s" and another shortened "%-14.7s".' printf: #('hello world' 'hello world'))! !

FormatDescriptor subclass: #NumberFormatDescriptor
        instanceVariableNames: 'operator padding radix space'
        classVariableNames: 'Base Radix'
        poolDictionaries: ''
        category: 'Printf'!

!NumberFormatDescriptor methodsFor: 'rendering' stamp: 'mir 6/6/2000 23:56'!
applyOperator: object
        "Character and Number are the only valid classes"

        | number string |
        object isNil ifTrue: [^'-'].
"object isInteger ifFalse: [self halt].
"       number := object asInteger.
        string := number printStringBase: self base.
        radix ifTrue: [string := self radixString , string].
        (space and: [operator == $d and: [number < 0]])
                ifTrue: [string := ' ' , string].
        ^ (width ~= 0 and: [string size > self stringLength])
                ifTrue: [String new: width withAll: $*]
                ifFalse: [string]! !


!NumberFormatDescriptor methodsFor: 'private'!
base
        ^ Base at: operator! !

!NumberFormatDescriptor methodsFor: 'private'!
padding
        ^ padding! !

!NumberFormatDescriptor methodsFor: 'private'!
radixString
        ^ Radix at: operator! !

!NumberFormatDescriptor methodsFor: 'private'!
setOperator: char
        operator := char! !

!NumberFormatDescriptor methodsFor: 'private'!
setPadding: paddingChar
        padding := paddingChar! !

!NumberFormatDescriptor methodsFor: 'private'!
stringLength
        ^precision isNil
                ifTrue: [SmallInteger maxVal]
                ifFalse: [precision]! !


!NumberFormatDescriptor methodsFor: 'initialize-release'!
initialize
        super initialize.
        padding := $ .
        radix := false.
        space := false! !


!NumberFormatDescriptor methodsFor: 'printing'!
printOn: aStream
        super printOn: aStream.
        padding == $0 ifTrue: [aStream nextPut: $0].
        radix ifTrue: [aStream nextPut: $#].
        space ifTrue: [aStream nextPut: $ ].
        self printWidthOn: aStream.
        aStream nextPut: operator! !


!NumberFormatDescriptor methodsFor: 'scanning'!
radix
        radix := true! !

!NumberFormatDescriptor methodsFor: 'scanning'!
space
        space := true! !

!NumberFormatDescriptor methodsFor: 'scanning'!
zero
        padding := $0! !

"-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- "!

NumberFormatDescriptor class
        instanceVariableNames: ''!

!NumberFormatDescriptor class methodsFor: 'class initialization'!
initialize
        "NumberFormatDescriptor initialize"
        Base := Dictionary new.
        Base at: $d put: 10.
        Base at: $o put: 8.
        Base at: $u put: 10.
        Base at: $x put: 16.
        Base at: $X put: 16.

        Radix := Dictionary new.
        Radix at: $d put: ''.
        Radix at: $o put: '0'.
        Radix at: $u put: ''.
        Radix at: $x put: '0x'.
        Radix at: $X put: '0X'.! !


!NumberFormatDescriptor class methodsFor: 'instance creation'!
newFrom: desc
        desc class == self ifTrue: [^ desc].
        ^ (super newFrom: desc) setPadding: desc padding! !

NumberFormatDescriptor subclass: #FloatFormatDescriptor
        instanceVariableNames: ''
        classVariableNames: ''
        poolDictionaries: ''
        category: 'Printf'!

!FloatFormatDescriptor methodsFor: 'rendering' stamp: 'hjo 9/18/2011 00:47'!
applyOperator: object
        "Number is the only valid class"

        | string |
        string := self zeroPaddedStringOfBase10ForFloat: object asFloat.
        string := string copyFrom: 1 to: ((string indexOf: $.) + (precision == 0
                                                ifTrue: [-1]
                                                ifFalse: [self precision]) min: string size).
        (space and: [object asFloat >= 0])
                ifTrue: [string := ' ' , string].
        ^(width ~= 0 and: [string size > width])
                ifTrue: [String new: width withAll: $*]
                ifFalse: [string]! !

!FloatFormatDescriptor methodsFor: 'rendering' stamp: 'hjo 9/18/2011 00:45'!
zeroPaddedStringOfBase10ForFloat: aFloat

        | stream |
        stream := String new writeStream.
        aFloat printOn: stream base: 10.
        stream next: self precision-1 put: $0.
        ^stream contents.
! !


!FloatFormatDescriptor methodsFor: 'private' stamp: 'mir 6/7/2000 00:01'!
digits
        ^ width == 0 ifTrue: [7] ifFalse: [width]! !

!FloatFormatDescriptor methodsFor: 'private'!
precision
        ^ precision isNil ifTrue: [1] ifFalse: [precision]! !

!FloatFormatDescriptor methodsFor: 'private'!
stringLength
        ^ width! !

FormatDescriptor subclass: #StringFormatDescriptor
        instanceVariableNames: ''
        classVariableNames: ''
        poolDictionaries: ''
        category: 'Printf'!

!StringFormatDescriptor methodsFor: 'rendering'!
applyOperator: object
        ^ object! !


!StringFormatDescriptor methodsFor: 'printing'!
printOn: aStream
        super printOn: aStream.
        self printWidthOn: aStream.
        aStream nextPut: $s! !

StringFormatDescriptor subclass: #PrintStringFormatDescriptor
        instanceVariableNames: ''
        classVariableNames: ''
        poolDictionaries: ''
        category: 'Printf'!

!PrintStringFormatDescriptor methodsFor: 'rendering'!
applyOperator: object
        ^ object printLeanString! !


!PrintStringFormatDescriptor methodsFor: 'printing'!
printOn: aStream
        aStream nextPut: $%.
        flush == #leftFlush ifTrue: [aStream nextPut: $-].
        self printWidthOn: aStream.
        aStream nextPut: $p! !

FormatDescriptor initialize!
NumberFormatDescriptor initialize!



Reply | Threaded
Open this post in threaded view
|

Re: Newbie Question: How does this work?

Joseph Alotta
In reply to this post by Joseph Alotta
Thanks for you help.  

I see that String has a #printf and the protocol is *printf,  and when the printf: method executes it looks everywhere for the FormatString class which is added to the end by the Printf package.

So the question is how did the *printf protocols get added to String?   It is not some magic by Monticello, because I did not use
Monticello, I just filein-ed the code.

So where in the code does it install the *printf methods to String?

Sincerely,

Joe.





On Oct 9, 2012, at 7:00 AM, [hidden email] wrote:

> On 12-10-08 6:13 PM, Joseph J Alotta wrote:
>> '%6.2e' printf: 412.343434
> I loaded in the code you added in Squeak 4.3, executed the above, and
> got "MessageNotUnderstood: ByteString>>printf:". I then loaded in Printf
> from squeaksource.com/Printf and it worked.
>
> Monticello added some extra methods to various classes in the image.
> Don't look at FormatString>>printf: Look at String>>printf:, which is a
> superclass of ByteString. The protocol list has printf with an * before
> it - *printf. The Monticello package added some extra methods to the
> String class.
>
> Chris
>
>
> Thank you for asking interesting questions and staying involved.
>
> One way to find this out for yourself is to run this code to step through
> what is happening (highlighting the two lines and hitting the PrintIt key
> combo):
> self halt.
> '123%s456' printf: ('s').
>
> You'll find that the class ByteString also now has a #printf:method
> inherited from String class. So, the ByteString class knows how to
> convert itself to a FormatString if it sees a #printf: method.
> printf: arguments
>       ^ self asFormatString printf: arguments
>
> The ' * ' in *printf method category name is a visual clue that a load has
> added it to the default String class.


Reply | Threaded
Open this post in threaded view
|

Re: Newbie Question: How does this work?

Frank Shearar-3
On 9 October 2012 16:08, Joseph J Alotta <[hidden email]> wrote:
> Thanks for you help.
>
> I see that String has a #printf and the protocol is *printf,  and when the printf: method executes it looks everywhere for the FormatString class which is added to the end by the Printf package.
>
> So the question is how did the *printf protocols get added to String?   It is not some magic by Monticello, because I did not use
> Monticello, I just filein-ed the code.
>
> So where in the code does it install the *printf methods to String?

The original code (in what's called "chunk format") was incomplete. If
it had been complete (like the Printf Chris Cunnington's mentioned)
you'd have seen something like this:

!String methodsFor: '*printf'!
printf: arguments
        ^ self asFormatString printf: arguments! !

In Monticello these chunks are instead represented by MCDefinition subclasses.

frank

> Sincerely,
>
> Joe.
>
>
>
>
>
> On Oct 9, 2012, at 7:00 AM, [hidden email] wrote:
>
>> On 12-10-08 6:13 PM, Joseph J Alotta wrote:
>>> '%6.2e' printf: 412.343434
>> I loaded in the code you added in Squeak 4.3, executed the above, and
>> got "MessageNotUnderstood: ByteString>>printf:". I then loaded in Printf
>> from squeaksource.com/Printf and it worked.
>>
>> Monticello added some extra methods to various classes in the image.
>> Don't look at FormatString>>printf: Look at String>>printf:, which is a
>> superclass of ByteString. The protocol list has printf with an * before
>> it - *printf. The Monticello package added some extra methods to the
>> String class.
>>
>> Chris
>>
>>
>> Thank you for asking interesting questions and staying involved.
>>
>> One way to find this out for yourself is to run this code to step through
>> what is happening (highlighting the two lines and hitting the PrintIt key
>> combo):
>> self halt.
>> '123%s456' printf: ('s').
>>
>> You'll find that the class ByteString also now has a #printf:method
>> inherited from String class. So, the ByteString class knows how to
>> convert itself to a FormatString if it sees a #printf: method.
>> printf: arguments
>>       ^ self asFormatString printf: arguments
>>
>> The ' * ' in *printf method category name is a visual clue that a load has
>> added it to the default String class.
>
>

Reply | Threaded
Open this post in threaded view
|

Re: Newbie Question: How does this work?

Joseph Alotta
In reply to this post by Joseph Alotta
I know I can make changes directly to String and add a method.   But I was wondering if I can
add a method to String and still keep the code in my Package.   (I've made my own package/category called JJA
that contains all the object that pertain to my program.)

So is there a way to create a method in my own category for a class in another category?





> Message: 4
> Date: Tue, 9 Oct 2012 16:16:59 +0100
> From: Frank Shearar <[hidden email]>
> Subject: Re: [squeak-dev] Re: Newbie Question: How does this work?
> To: The general-purpose Squeak developers list
> <[hidden email]>
> Message-ID:
> <CAJbhyRH3WgsMDdYWMvnkdBF770kV=nqOOyxz1ysYrk=[hidden email]>
> Content-Type: text/plain; charset=ISO-8859-1
>
> On 9 October 2012 16:08, Joseph J Alotta <[hidden email]> wrote:
>> Thanks for you help.
>>
>> I see that String has a #printf and the protocol is *printf,  and when the printf: method executes it looks everywhere for the FormatString class which is added to the end by the Printf package.
>>
>> So the question is how did the *printf protocols get added to String?   It is not some magic by Monticello, because I did not use
>> Monticello, I just filein-ed the code.
>>
>> So where in the code does it install the *printf methods to String?
>
> The original code (in what's called "chunk format") was incomplete. If
> it had been complete (like the Printf Chris Cunnington's mentioned)
> you'd have seen something like this:
>
> !String methodsFor: '*printf'!
> printf: arguments
>        ^ self asFormatString printf: arguments! !
>
> In Monticello these chunks are instead represented by MCDefinition subclasses.
>
> frank


Reply | Threaded
Open this post in threaded view
|

Re: Newbie Question: How does this work?

Chris Cunnington
On 12-10-10 12:07 PM, Joseph J Alotta wrote:

> I know I can make changes directly to String and add a method.   But I was wondering if I can
> add a method to String and still keep the code in my Package.   (I've made my own package/category called JJA
> that contains all the object that pertain to my program.)
>
> So is there a way to create a method in my own category for a class in another category?
>
>
>
>
>
>> Message: 4
>> Date: Tue, 9 Oct 2012 16:16:59 +0100
>> From: Frank Shearar <[hidden email]>
>> Subject: Re: [squeak-dev] Re: Newbie Question: How does this work?
>> To: The general-purpose Squeak developers list
>> <[hidden email]>
>> Message-ID:
>> <CAJbhyRH3WgsMDdYWMvnkdBF770kV=nqOOyxz1ysYrk=[hidden email]>
>> Content-Type: text/plain; charset=ISO-8859-1
>>
>> On 9 October 2012 16:08, Joseph J Alotta <[hidden email]> wrote:
>>> Thanks for you help.
>>>
>>> I see that String has a #printf and the protocol is *printf,  and when the printf: method executes it looks everywhere for the FormatString class which is added to the end by the Printf package.
>>>
>>> So the question is how did the *printf protocols get added to String?   It is not some magic by Monticello, because I did not use
>>> Monticello, I just filein-ed the code.
>>>
>>> So where in the code does it install the *printf methods to String?
>> The original code (in what's called "chunk format") was incomplete. If
>> it had been complete (like the Printf Chris Cunnington's mentioned)
>> you'd have seen something like this:
>>
>> !String methodsFor: '*printf'!
>> printf: arguments
>>         ^ self asFormatString printf: arguments! !
>>
>> In Monticello these chunks are instead represented by MCDefinition subclasses.
>>
>> frank
>
Go to class String. Right click on the protocol (third pane from the
left) at "--all--". Select "add category". Select "new".
Type "*jja" and accept. Drag your method into that protocol category.

Chris