--- a/CharacterArray.st Sun Aug 11 23:49:41 2019 +0200
+++ b/CharacterArray.st Mon Aug 12 11:52:59 2019 +0200
@@ -7686,11 +7686,14 @@
ignoreNonNumericEscapes:true
ignoreSpecialEscapes:true
requireParentheses:true
+ ifKeyAbsent:nil
on:stream.
^ stream contents.
"
'hello %1' expandNumericPlaceholdersWith:#('world')
+ 'hello %(1)' expandNumericPlaceholdersWith:#('world')
+ 'hello %(10)' expandNumericPlaceholdersWith:#('world')
'hello %1 %abc' expandNumericPlaceholdersWith:#('world')
'hello %1 %(abc)' expandNumericPlaceholdersWith:#('world')
"
@@ -7705,11 +7708,30 @@
"Modified: / 14-07-2018 / 09:23:31 / Claus Gittinger"
!
+expandPlaceholders
+ "return a copy of the receiver, where %<special> escapes are expanded.
+ %<..>
+ Insert a character constant or character sequence, being one of:
+ cr nl tab return lf crlf ff null backspace bell esc newPage space
+ i.e. you can use %<cr> to insert a CR, and %<tab> to insert a TAB.
+
+ See also bindWith:... for VisualAge compatibility."
+
+ ^ self expandPlaceholdersWith:nil
+
+ "
+ 'hello a%<crlf>b' expandPlaceholders
+ "
+
+ "Modified: / 01-07-1997 / 00:53:24 / cg"
+ "Modified: / 14-07-2018 / 09:23:31 / Claus Gittinger"
+!
+
expandPlaceholders:escapeCharacter with:argArrayOrDictionary
- "this is the generic version of the old %-escaping method, allowing for an arbitrary
+ "this is a more general version of the old %-escaping method, allowing for an arbitrary
escape character to be used (typically $$ or $% are effectively used).
- Return a copy of the receiver, where all %i escapes are
+ Returns a copy of the receiver, where all %i escapes are
replaced by corresponding arguments' printStrings from the argArrayOrDictionary.
I.e. 'hello %1; how is %2' expandPlaceholdersWith:#('world' 'this') results
in the new string 'hello world; how is this'.
@@ -7730,6 +7752,7 @@
ignoreNonNumericEscapes:false
ignoreSpecialEscapes:false
requireParentheses:true
+ ifKeyAbsent:nil
on:stream.
^ stream contents.
@@ -7805,6 +7828,118 @@
ignoreNonNumericEscapes:ignoreNonNumericEscapes
ignoreSpecialEscapes:ignoreSpecialEscapes
requireParentheses:requireParentheses
+ ifKeyAbsent:nil
+ on:stream.
+ ^ stream contents
+
+ "
+ 'hello %1' expandPlaceholders:$% with:#('world') on:Transcript.
+ 'hello %a%<cr>' expandPlaceholders:$%
+ with:(Dictionary new at:'a' put:'world';yourself)
+ on:Transcript.
+ 'hello %aa%<cr>' expandPlaceholders:$% with:(Dictionary new at:'a' put:'world';yourself) on:Transcript.
+ 'hello %(aa)%<cr>' expandPlaceholders:$% with:(Dictionary new at:'aa' put:'world';yourself) on:Transcript.
+ 'hello %aa%<cr>' expandPlaceholders:$% with:(Dictionary new at:'aa' put:'world';yourself) ignoreNumericEscapes:true requireParentheses:false on:Transcript.
+
+ String streamContents:[:s|
+ 'hello %1' expandPlaceholders:$% with:#('world') on:s.
+ s cr.
+ 'hello $1; how is $2' expandPlaceholders:$$ with:#('world' 'this') on:s.
+ s cr.
+ 'hello %2; how is %1' expandPlaceholders:$% with:#('world' 'this') on:s.
+ s cr.
+ '%1 plus %2 gives %3 ' expandPlaceholders:$% with:#(4 5 9) on:s.
+ s cr.
+ '%%(1)0 gives %(1)0' expandPlaceholders:$% with:#(123) on:s.
+ s cr.
+ '%%10 gives %10' expandPlaceholders:$% with:#(123) on:s.
+ s cr.
+ '%%(10) gives %(10) %<cr>%<tab>next line' expandPlaceholders:$% with:#(123) on:s.
+ s cr.
+ '%%test gives %test' expandPlaceholders:$% with:#(123) on:s.
+ s cr.
+ '|%%<tab>|%%1|%%<cr>| gives |%<tab>|%1|%<cr>|' expandPlaceholders:$% with:#(foo) on:s.
+ ]
+ "
+
+ "without xlation dictionary:
+ 'hello %1' expandPlaceholdersWith:nil.
+ 'hello%<cr> %1' expandPlaceholdersWith:nil.
+ "
+
+ "
+ |dict|
+
+ dict := Dictionary new.
+ dict at:1 put:'one'.
+ dict at:$a put:'AAAAA'.
+ dict at:$b put:[ Time now ].
+ String streamContents:[:s|
+ 'hello $1 $a $b' expandPlaceholders:$$ with:dict on:s.
+ ].
+ "
+
+ "using blocks:
+ |dict|
+
+ dict := Dictionary new.
+ dict at:'time' put:[Time now printString].
+ dict at:'date' put:[Date today printString].
+ String streamContents:[:s|
+ 'it is $(time) $(date)' expandPlaceholders:$$ with:dict on:s.
+ ].
+ "
+
+ "Created: / 14-01-2019 / 17:43:03 / Claus Gittinger"
+ "Modified: / 05-06-2019 / 17:05:47 / Claus Gittinger"
+!
+
+expandPlaceholders:escapeCharacter with:argArrayOrDictionary
+ ignoreNumericEscapes:ignoreNumericEscapes
+ ignoreNonNumericEscapes:ignoreNonNumericEscapes
+ ignoreSpecialEscapes:ignoreSpecialEscapes
+ requireParentheses:requireParentheses
+ ifKeyAbsent:ifKeyAbsentBlockOrNil
+
+ "this is the generic version of the old %-escaping method, allowing for an arbitrary
+ escape character to be used (typically $$ or $% are effectively used).
+
+ Return a copy of the receiver, where all %i escapes are
+ replaced by corresponding arguments' printStrings from the argArray.
+ I.e. 'hello %1; how is %2' expandPlaceholdersWith:#('world' 'this') results
+ in the new string 'hello world; how is this'.
+
+ As an extension, the argument may also be a dictionary, providing values for symbolic keys.
+ In this case, %a .. %z and %(...) are also allowed.
+ (%1..%9 require a numeric key in the dictionary, however)
+
+ Also, values in argArrayOrDictionary may be blocks.
+
+ To get a '%' character, use a '%%'-escape.
+ To get an integer-indexed placeHolder followed by another digit,
+ or an index > 9, you must use %(digit).
+
+ See also bindWith:... for VisualAge compatibility.
+ Use %<cr> to insert a CR and %<tab> to insert a TAB.
+
+ ignoreNumericEscapes controls if %<nr> escapes are expanded or not.
+ This is required for Windows batch-script expansion, where %<nr> should be left unchanged.
+
+ requireParentheses controls if $abc is allowed or not.
+ If true, multi-character replacements need to be parenthized as $(abc);
+ if false, you can also write $abc.
+ "
+
+ |stream|
+
+ stream := (TextStream ? CharacterWriteStream) on:(self species uninitializedNew:self size + 20).
+ self
+ expandPlaceholders:escapeCharacter with:argArrayOrDictionary
+ ignoreNumericEscapes:ignoreNumericEscapes
+ ignoreNonNumericEscapes:ignoreNonNumericEscapes
+ ignoreSpecialEscapes:ignoreSpecialEscapes
+ requireParentheses:requireParentheses
+ ifKeyAbsent:ifKeyAbsentBlockOrNil
on:stream.
^ stream contents
@@ -7874,7 +8009,8 @@
ignoreNumericEscapes:ignoreNumericEscapes
ignoreNonNumericEscapes:ignoreNonNumericEscapes
ignoreSpecialEscapes:ignoreSpecialEscapes
- requireParentheses:requireParentheses
+ requireParentheses:requireParentheses
+ ifKeyAbsent:replaceActionOrNil
on:aStream
"this is the central method for %-escaping, allowing for an arbitrary
@@ -7896,24 +8032,49 @@
or an index > 9, you must use %(digit).
See also bindWith:... for VisualAge compatibility.
- Use %<cr> to insert a CR and %<tab> to insert a TAB.
-
- ignoreNumericEscapes controls if %<nr> escapes are expanded or not.
- This is required for Windows batch-script expansion, where %<nr> should be left unchanged.
-
- ignoreSpecialEscapes controls if control characters like %<cr> are expanded or not.
-
- requireParentheses controls if $abc is allowed or not.
- If true, multi-character replacements need to be parenthized as $(abc) and the above is
- interpreted as $(a)bc;
- if false, you can also write $abc.
+
+ - %<..>
+ Insert a character constant or character sequence, being one of:
+ cr nl tab return lf crlf ff null backspace bell esc newPage space
+ i.e. you can use %<cr> to insert a CR, and %<tab> to insert a TAB.
+
+ - ignoreNumericEscapes
+ controls if %<nr> escapes are expanded or not.
+ This is required for Windows batch-script expansion, where %<nr> should be left unchanged.
+
+ - ignoreSpecialEscapes
+ controls if control characters like %<cr> are expanded or not.
+
+ - requireParentheses
+ controls if $abc is allowed or not.
+ If true, multi-character replacements need to be parenthized as $(abc),
+ and the above is interpreted as $(a)bc
+ If false, you can also write $abc.
+
+ - keepIfNoSuchKey
+ controls what should happen if a variable/index is encountered which is not found in argArrayOrDictionary.
+ It can be nil or a two arg block.
+ If nil, the sequence is replaced by an empty string (i.e. 'abc$(foo)def' -> 'abcdef')
+ if aBlock, it will be called with both the full escape sequence and the cariable only as arguments,
+ and the expansion will be what the block returns.
+ i.e. if the block is [:meta :name | meta], then the above will result in 'abc$(foo)def'
+ and if the block is [:meta :name | name], then the above will result in 'abcfoodef'
+ Usefull if you want to expand a string twice, without loosing the key-sequences in the first place.
+ Notice: for supid backward compatibility, keepIfNoSuchKey is not applied for %X sequences, where X is a single letter.
"
|next v key numericKey
idx "{ SmallInteger }"
idx2 "{ SmallInteger }"
start "{ SmallInteger }"
- stop "{ SmallInteger }"|
+ stop "{ SmallInteger }"
+ noReplacementAction|
+
+ replaceActionOrNil isNil ifTrue:[
+ noReplacementAction := [:seq :var | '']
+ ] ifFalse:[
+ noReplacementAction := replaceActionOrNil
+ ].
stop := self size.
start := 1.
@@ -7942,19 +8103,29 @@
key := self copyFrom:idx+2 to:idx2-1.
idx := idx2 - 1.
key := key asSymbolIfInterned.
- (#(cr tab nl return lf ff null) includesIdentical:key) ifTrue:[
+ (#(cr tab nl return lf ff null backspace bell esc newPage space) includesIdentical:key) ifTrue:[
aStream nextPut:(Character perform:key).
+ ] ifFalse:[
+ (key == #crlf) ifTrue:[
+ aStream nextPutAll:(String crlf).
+ ] ifFalse:[
+ aStream nextPutAll:key.
+ ]
].
].
] ifFalse:[
argArrayOrDictionary isNil ifTrue:[
- aStream nextPut:escapeCharacter.
+ "/ %x but no dictionary provided (strange error case, actually)
aStream nextPut:next.
] ifFalse:[
(next isDigit and:[ignoreNumericEscapes not]) ifTrue:[
- v := argArrayOrDictionary at:(next digitValue) ifAbsent:''
+ "/ %N (N is digit)
+ v := argArrayOrDictionary
+ at:(next digitValue)
+ ifAbsent:[ noReplacementAction value:escapeCharacter,next value:next ]
] ifFalse:[
next == $( ifTrue:[
+ "/ %(name)
idx2 := self indexOf:$) startingAt:idx+2.
self assert:(idx2 > 0) message:'closing parenthesis missing'.
key := self copyFrom:idx+2 to:idx2-1.
@@ -7966,7 +8137,9 @@
ignoreNumericEscapes ifTrue:[
v := escapeCharacter,'(',key,')'
] ifFalse:[
- v := argArrayOrDictionary at:numericKey ifAbsent:''
+ v := argArrayOrDictionary
+ at:numericKey
+ ifAbsent:[ noReplacementAction value:escapeCharacter,'(',key,')' value:key ]
]
] ifFalse:[
ignoreNonNumericEscapes ifTrue:[
@@ -7985,7 +8158,7 @@
(key size == 1 and:[ argArrayOrDictionary includesKey:(key at:1)]) ifTrue:[
v := argArrayOrDictionary at:(key at:1)
] ifFalse:[
- v := ''
+ v := noReplacementAction value:escapeCharacter,'(',key,')' value:key
]
].
].
@@ -7997,7 +8170,7 @@
and:[ next isLetter
and:[ argArrayOrDictionary isSequenceable not "is a Dictionary"]]
) ifTrue:[
- "so next is a non-numeric single character."
+ "%X (X is letter)"
requireParentheses ifTrue:[
key := next.
] ifFalse:[
@@ -8016,7 +8189,9 @@
"try symbol or string instead of character"
argArrayOrDictionary
at:key asString asSymbolIfInternedOrSelf
- ifAbsent:[escapeCharacter asString , key].
+ ifAbsent:[
+ escapeCharacter asString , key
+ ].
].
].
] ifFalse:[
@@ -8097,6 +8272,57 @@
"Modified: / 05-06-2019 / 17:05:47 / Claus Gittinger"
!
+expandPlaceholders:escapeCharacter
+ with:argArrayOrDictionary
+ ignoreNumericEscapes:ignoreNumericEscapes
+ ignoreNonNumericEscapes:ignoreNonNumericEscapes
+ ignoreSpecialEscapes:ignoreSpecialEscapes
+ requireParentheses:requireParentheses
+ on:aStream
+
+ "this is the central method for %-escaping, allowing for an arbitrary
+ escape character to be used (typically $$ or $% are effectively used).
+
+ Write the receiver to aStream, where all %i escapes are
+ replaced by corresponding arguments' printStrings from the argArray.
+ I.e. 'hello %1; how is %2' expandPlaceholdersWith:#('world' 'this') results
+ in the new string 'hello world; how is this'.
+
+ As an extension, the argument may also be a dictionary, providing values for symbolic keys.
+ In this case, %a .. %z and %(...) are also allowed.
+ (%1..%9 require a numeric key in the dictionary, however)
+
+ Also, values in argArrayOrDictionary may be blocks.
+
+ To get a '%' character, use a '%%'-escape.
+ To get an integer-indexed placeHolder followed by another digit,
+ or an index > 9, you must use %(digit).
+
+ See also bindWith:... for VisualAge compatibility.
+ Use %<cr> to insert a CR and %<tab> to insert a TAB.
+
+ ignoreNumericEscapes controls if %<nr> escapes are expanded or not.
+ This is required for Windows batch-script expansion, where %<nr> should be left unchanged.
+
+ ignoreSpecialEscapes controls if control characters like %<cr> are expanded or not.
+
+ requireParentheses controls if $abc is allowed or not.
+ If true, multi-character replacements need to be parenthized as $(abc) and the above is
+ interpreted as $(a)bc;
+ if false, you can also write $abc.
+ "
+
+ ^ self
+ expandPlaceholders:escapeCharacter
+ with:argArrayOrDictionary
+ ignoreNumericEscapes:ignoreNumericEscapes
+ ignoreNonNumericEscapes:ignoreNonNumericEscapes
+ ignoreSpecialEscapes:ignoreSpecialEscapes
+ requireParentheses:requireParentheses
+ ifKeyAbsent:nil
+ on:aStream
+!
+
expandPlaceholders:escapeCharacter with:argArrayOrDictionary
ignoreNumericEscapes:ignoreNumericEscapes
on:aStream
@@ -8132,6 +8358,7 @@
ignoreNonNumericEscapes:false
ignoreSpecialEscapes:false
requireParentheses:true
+ ifKeyAbsent:nil
on:aStream
"
@@ -8188,46 +8415,37 @@
"Modified (comment): / 14-01-2019 / 17:44:02 / Claus Gittinger"
!
-expandPlaceholders:escapeCharacter with:argArrayOrDictionary
+expandPlaceholders:escapeCharacter
+ with:argArrayOrDictionary
ignoreNumericEscapes:ignoreNumericEscapes
requireParentheses:requireParentheses
- "this is the generic version of the old %-escaping method, allowing for an arbitrary
+
+ "this is a more general version of the old %-escaping method, allowing for an arbitrary
escape character to be used (typically $$ or $% are effectively used).
-
- Return a copy of the receiver, where all %i escapes are
- replaced by corresponding arguments' printStrings from the argArray.
- I.e. 'hello %1; how is %2' expandPlaceholdersWith:#('world' 'this') results
- in the new string 'hello world; how is this'.
-
- As an extension, the argument may also be a dictionary, providing values for symbolic keys.
- In this case, %a .. %z and %(...) are also allowed.
- (%1..%9 require a numeric key in the dictionary, however)
-
- Also, values in argArrayOrDictionary may be blocks.
-
- To get a '%' character, use a '%%'-escape.
- To get an integer-indexed placeHolder followed by another digit,
- or an index > 9, you must use %(digit).
-
See also bindWith:... for VisualAge compatibility.
- Use %<cr> to insert a CR and %<tab> to insert a TAB.
-
- ignoreNumericEscapes controls if %<nr> escapes are expanded or not.
- This is required for Windows batch-script expansion, where %<nr> should be left unchanged.
-
- requireParentheses controls if $abc is allowed or not.
- If true, multi-character replacements need to be parenthized as $(abc);
- if false, you can also write $abc.
+
+ - ignoreNumericEscapes
+ controls if %<nr> escapes are expanded or not.
+ This is required for Windows batch-script expansion, where %<nr> should be left unchanged.
+
+ - requireParentheses
+ controls if $abc is allowed or not.
+ If true, multi-character replacements need to be parenthized as $(abc);
+ if false, you can also write $abc.
"
|stream|
stream := (TextStream ? CharacterWriteStream) on:(self species uninitializedNew:self size + 20).
- self expandPlaceholders:escapeCharacter with:argArrayOrDictionary
+ self
+ expandPlaceholders:escapeCharacter
+ with:argArrayOrDictionary
ignoreNumericEscapes:ignoreNumericEscapes
ignoreNonNumericEscapes:false
ignoreSpecialEscapes:false
- requireParentheses:requireParentheses on:stream.
+ requireParentheses:requireParentheses
+ ifKeyAbsent:nil
+ on:stream.
^ stream contents
"
@@ -8326,11 +8544,14 @@
if false, you can also write $abc.
"
- self expandPlaceholders:escapeCharacter with:argArrayOrDictionary
+ self
+ expandPlaceholders:escapeCharacter
+ with:argArrayOrDictionary
ignoreNumericEscapes:ignoreNumericEscapes
ignoreNonNumericEscapes:false
ignoreSpecialEscapes:false
requireParentheses:requireParentheses
+ ifKeyAbsent:nil
on:aStream.
"
@@ -8423,6 +8644,7 @@
ignoreNonNumericEscapes:false
ignoreSpecialEscapes:false
requireParentheses:true
+ ifKeyAbsent:nil
on:aStream
"
@@ -8503,6 +8725,7 @@
ignoreNonNumericEscapes:false
ignoreSpecialEscapes:false
requireParentheses:true
+ ifKeyAbsent:nil
on:stream.
^ stream contents.
@@ -8538,6 +8761,38 @@
"Modified: / 14-07-2018 / 09:23:31 / Claus Gittinger"
!
+expandPlaceholdersWith:argArrayOrDictionary ifKeyAbsent:ifNoSuchKeyActionOrNil
+ "return a copy of the receiver, where all %i escapes are
+ replaced by corresponding arguments' printStrings from the argArrayOrDictionary.
+ I.e. 'hello %1; how is %2' expandPlaceholdersWith:#('world' 'this') results
+ in the new string 'hello world; how is this'.
+ The argument may also be a dictionary, providing values for symbolic keys.
+ To get a '%' character, use a '%%'-escape.
+
+ See the comment in
+ expandPlaceholders:with:ignoreNumericEscapes:ignoreNonNumericEscapes:ignoreSpecialEscapes:requireParentheses:ifKeyAbsent:on:
+ for a full explanation.
+
+ See also bindWith:... for VisualAge compatibility."
+
+ |stream|
+
+ stream := (TextStream ? CharacterWriteStream) on:(self species uninitializedNew:self size + 20).
+ self
+ expandPlaceholders:$% with:argArrayOrDictionary
+ ignoreNumericEscapes:false
+ ignoreNonNumericEscapes:false
+ ignoreSpecialEscapes:false
+ requireParentheses:true
+ ifKeyAbsent:ifNoSuchKeyActionOrNil
+ on:stream.
+ ^ stream contents.
+
+ "
+ 'hello %(abc) %1 %a %; %%' expandPlaceholdersWith:nil ifNoSuchKey:[:str :nm | str]
+ "
+!
+
expandPlaceholdersWith:argArrayOrDictionary on:aStream
"write the receiver to aStream, where all %i escapes are
replaced by corresponding arguments' printStrings from the argArrayOrDictionary.
@@ -8563,6 +8818,7 @@
ignoreNonNumericEscapes:false
ignoreSpecialEscapes:false
requireParentheses:true
+ ifKeyAbsent:nil
on:aStream
"