Sorry, I tried to have them included at one point on squeak but I was talked down.
I use them a lot! They work with other things besides strings. Collection >> explode: aDelimiter "explode the collection into a collection of collections broken by aDelimiter" "(#(#(1 2) #(3 4)) mergeDelimited: Character tab ) explode: Character tab = an OrderedCollection(#(1 2) #(3 4)) 'abcdef' explode: 'cd' = an OrderedCollection('ab' 'ef')" | resultCollection starting aDelimiterPosition aDelimiterSize | self ifEmpty: [^self]. resultCollection := OrderedCollection new. aDelimiterSize := aDelimiter isCollection ifTrue: [aDelimiter size] ifFalse: [1]. starting := 1. [aDelimiterPosition := aDelimiter isCollection ifTrue: [self indexOfSubCollection: aDelimiter startingAt: starting] ifFalse: [self indexOf: aDelimiter startingAt: starting ifAbsent: [0]]. aDelimiterPosition > 0] whileTrue: [ resultCollection add: (self copyFrom: starting to: aDelimiterPosition - 1). starting := aDelimiterPosition + aDelimiterSize. ]. resultCollection add: (self copyFrom: starting to: self size). ^resultCollection Collection >> mergeDelimited: anObject "return to reciever a collection with each element concatenated to remove imbeded collections" "#(#(1 2) #(3 4)) mergeDelimited: Character tab = #(1 2 Character tab 3 4), #('ab' 'cd') mergeDelimited: Character cr = 'ab cd' " | returnCollection aSeperator | self ifEmpty: [^self]. aSeperator := anObject isCollection ifTrue: [anObject] ifFalse: [Array with: anObject]. returnCollection := self first species new. self copy from: 1 to: self size -1 do: [:a | a ifNotNil: [ returnCollection := returnCollection, a, aSeperator ]. ]. ^returnCollection, self last On Thu, Sep 28, 2017 at 11:25 AM, Sven Van Caekenberghe <s...@stfx.eu> wrote: > > > > On 28 Sep 2017, at 17:13, Thierry Goubier <thierry.goub...@gmail.com> > wrote: > > > > > > > > 2017-09-28 17:08 GMT+02:00 Sven Van Caekenberghe <s...@stfx.eu>: > > > > > > > On 28 Sep 2017, at 16:58, p...@highoctane.be wrote: > > > > > > I stand corrected. > > > > > > Nuclear style feature then. > > > > > > 'Let me get out of here {Smalltalk snapshot:false andQuit: true}. > BOOM' interpolate > > > > > > Feels like > > > > > > STR='sudo reboot'; $(STR) > > > > > > in bash when one is sudoer. > > > > Yeah, but it is always the original programmer who writes the template > (including the 'Smalltalk snapshot:false andQuit: true' and the 'sudo > reboot' in you examples), you would not do that for any good reason. > > > > Typically, you would write something like > > > > 'My name is {firstname} {lastname}' interpolate. > > > > in a method of an object with firstname and lastname as instance > variables. To access the binding, #evaluate: is used. Though a malicious > person could enter 'Smalltalk snapshot:false andQuit: true' as name, it is > not that expression that gets evaluated. So in that respect there is no > risk. > > > > The risk would be when the template string itself would be (partially) > based on used input. > > > > which is easy to overlook: > > > > aString interpolate > > > > People are on average a bit more carefull when they use #compile: or > #evaluate:. > > > > Thierry > > 100% correct, hence my warning. > > > > Phil > > > > > > On Thu, Sep 28, 2017 at 4:43 PM, Sven Van Caekenberghe <s...@stfx.eu> > wrote: > > > > > > > > > > On 28 Sep 2017, at 16:27, p...@highoctane.be wrote: > > > > > > > > We also have http://norbert.hartl.name/blog/2013/10/03/mustache- > templates-for-smalltalk/ > > > > > > > > Phil > > > > > > Yes, Mustache is a cool templating engine, but it is similar to > #format: not to #interpolate. With true string interpolation, you do not > provide a context, you just write the expressions inline. Compare the > following two: > > > > > > 'Today is {1} format: { Date today }. > > > > > > 'Today is { Date today }' interpolate. > > > > > > > On Thu, Sep 28, 2017 at 4:20 PM, Sven Van Caekenberghe <s...@stfx.eu> > wrote: > > > > Hi, > > > > > > > > I got into a little office discussion about string interpolation as > it is done in different programming languages. > > > > > > > > In Pharo we have String>>#format: which is pretty nice. It works as > follows: > > > > > > > > | x y | > > > > x := 123. > > > > y := #foo. > > > > 'x={1} and y={2}' format: { x. y }. > > > > > > > > It is also possible to use a dictionary with keys, like this: > > > > > > > > | x y | > > > > x := 123. > > > > y := #foo. > > > > 'x={x} and y={y}' format: { #x->x. #y->y } asDictionary. > > > > > > > > But this is not true string interpolation as described in [ > https://en.wikipedia.org/wiki/String_interpolation ]. The idea is to > write the value generating expressions directly inside the strings. > > > > > > > > Since in Pharo we add features not by extending the syntax but by > adding messages I wondered if it could be done for string interpolation. > The goal is to make the following work: > > > > > > > > | x y | > > > > x := 123. > > > > y := #foo. > > > > 'It seems x equals {x} and y equals {y} while Pi is still {Float > pi}' interpolate. > > > > > > > > => 'It seems x equals 123 and y equals foo while Pi is still > 3.141592653589793' > > > > > > > > Here is the implementation I came up with: > > > > > > > > String>>#interpolate > > > > "Format the receiver by interpolating the evaluation of expressions > > > > in between curly brackets in the context of the sender as in the > following 3 oneline examples. > > > > 'Today is {Date today}' interpolate. > > > > | x | x := 123. 'x equals {x} and pi equals {Float pi}' > interpolate. > > > > 'In {#strings} you can escape \{ by prefixing it with \\' > interpolate." > > > > > > > > | senderContext | > > > > senderContext := thisContext sender. > > > > ^ self class new: self size streamContents: [ :out | | stream | > > > > stream := self readStream. > > > > [ stream atEnd ] whileFalse: [ | currentChar | > > > > (currentChar := stream next) == ${ > > > > ifTrue: [ | expression result | > > > > expression := stream upTo: $}. > > > > result := Compiler new > > > > evaluate: expression in: senderContext to: nil > notifying: nil ifFail: [ ^ nil ] logged: false. > > > > out nextPutAll: result asString ] > > > > ifFalse: [ > > > > currentChar == $\ > > > > ifTrue: [ stream atEnd ifFalse: [ out nextPut: stream > next ] ] > > > > ifFalse: [ out nextPut: currentChar ] ] ] ] > > > > > > > > It is a hack that could certainly be improved. And there is of > course an obvious security problem. > > > > > > > > Thoughts ? > > > > > > > > Sven > > > > > > > > > > > > > > > > > > > > > > > > >