varmod-subst.mk revision 1.17 1 # $NetBSD: varmod-subst.mk,v 1.17 2025/03/29 19:08:53 rillig Exp $
2 #
3 # Tests for the :S,from,to, variable modifier.
4
5 all: mod-subst
6 all: mod-subst-delimiter
7 all: mod-subst-chain
8 all: mod-subst-dollar
9
10 WORDS= sequences of letters
11
12 # The empty pattern never matches anything, except if it is anchored at the
13 # beginning or the end of the word.
14 .if ${WORDS:S,,,} != ${WORDS}
15 . error
16 .endif
17
18 # The :S modifier flag '1' is applied exactly once.
19 .if ${WORDS:S,e,*,1} != "s*quences of letters"
20 . error
21 .endif
22
23 # The :S modifier flag '1' is applied to the first occurrence, no matter if
24 # the occurrence is in the first word or not.
25 .if ${WORDS:S,f,*,1} != "sequences o* letters"
26 . error
27 .endif
28
29 # The :S modifier replaces every first match per word.
30 .if ${WORDS:S,e,*,} != "s*quences of l*tters"
31 . error
32 .endif
33
34 # The :S modifier flag 'g' replaces every occurrence.
35 .if ${WORDS:S,e,*,g} != "s*qu*nc*s of l*tt*rs"
36 . error
37 .endif
38
39 # The '^' in the search pattern anchors the pattern at the beginning of each
40 # word, thereby matching a prefix.
41 .if ${WORDS:S,^sequ,occurr,} != "occurrences of letters"
42 . error
43 .endif
44
45 # The :S modifier with a '^' anchor replaces the whole word if that word is
46 # exactly the pattern.
47 .if ${WORDS:S,^of,with,} != "sequences with letters"
48 . error
49 .endif
50
51 # The :S modifier does not match if the pattern is longer than the word.
52 .if ${WORDS:S,^office,does not match,} != ${WORDS}
53 . warning
54 .endif
55
56 # The '$' in the search pattern anchors the pattern at the end of each word,
57 # thereby matching a suffix.
58 .if ${WORDS:S,f$,r,} != "sequences or letters"
59 . error
60 .endif
61
62 # The :S modifier with a '$' anchor replaces at most one occurrence per word.
63 .if ${WORDS:S,s$,,} != "sequence of letter"
64 . error
65 .endif
66
67 # The :S modifier with a '$' anchor replaces the whole word if that word is
68 # exactly the pattern.
69 .if ${WORDS:S,of$,,} != "sequences letters"
70 . error
71 .endif
72
73 # The :S modifier with a '$' anchor and a pattern that is longer than a word
74 # cannot match that word.
75 .if ${WORDS:S,eof$,,} != ${WORDS}
76 . warning
77 .endif
78
79 # The :S modifier with the '^' and '$' anchors matches an exact word.
80 .if ${WORDS:S,^of$,,} != "sequences letters"
81 . error
82 .endif
83
84 # The :S modifier with the '^' and '$' anchors does not match a word that
85 # starts with the pattern but is longer than the pattern.
86 .if ${WORDS:S,^o$,,} != ${WORDS}
87 . error
88 .endif
89
90 # The :S modifier with the '^' and '$' anchors does not match a word that ends
91 # with the pattern but is longer than the pattern.
92 .if ${WORDS:S,^f$,,} != ${WORDS}
93 . error
94 .endif
95
96 # The :S modifier with the '^' and '$' anchors does not match a word if the
97 # pattern ends with the word but is longer than the word.
98 .if ${WORDS:S,^eof$,,} != ${WORDS}
99 . error
100 .endif
101
102 # The :S modifier with the '^' and '$' anchors does not match a word if the
103 # pattern starts with the word but is longer than the word.
104 .if ${WORDS:S,^office$,,} != ${WORDS}
105 . error
106 .endif
107
108 # Except for the '^' and '$' anchors, the pattern does not contain any special
109 # characters, so the '*' from the pattern would only match a literal '*' in a
110 # word.
111 .if ${WORDS:S,*,replacement,} != ${WORDS}
112 . error
113 .endif
114
115 # Except for the '^' and '$' anchors, the pattern does not contain any special
116 # characters, so the '.' from the pattern would only match a literal '.' in a
117 # word.
118 .if ${WORDS:S,.,replacement,} != ${WORDS}
119 . error
120 .endif
121
122 # The '&' in the replacement is a placeholder for the text matched by the
123 # pattern.
124 .if ${:Uvalue:S,^val,&,} != "value"
125 . error
126 .endif
127 .if ${:Uvalue:S,ue$,&,} != "value"
128 . error
129 .endif
130 .if ${:Uvalue:S,^val,&-&-&,} != "val-val-value"
131 . error
132 .endif
133 .if ${:Uvalue:S,ue$,&-&-&,} != "value-ue-ue"
134 . error
135 .endif
136
137
138 # When a word is replaced with nothing, the remaining words are separated by a
139 # single space, not two.
140 .if ${1 2 3:L:S,2,,} != "1 3"
141 . error
142 .endif
143
144
145 # In an empty expression, the ':S' modifier matches a single time, but only if
146 # the search string is empty and anchored at either the beginning or the end
147 # of the word.
148 .if ${:U:S,,out-of-nothing,} != ""
149 . error
150 .endif
151 .if ${:U:S,^,out-of-nothing,} != "out-of-nothing"
152 . error
153 .endif
154 .if ${:U:S,$,out-of-nothing,} != "out-of-nothing"
155 . error
156 .endif
157 .if ${:U:S,^$,out-of-nothing,} != "out-of-nothing"
158 . error
159 .endif
160 .if ${:U:S,,out-of-nothing,g} != ""
161 . error
162 .endif
163 .if ${:U:S,^,out-of-nothing,g} != "out-of-nothing"
164 . error
165 .endif
166 .if ${:U:S,$,out-of-nothing,g} != "out-of-nothing"
167 . error
168 .endif
169 .if ${:U:S,^$,out-of-nothing,g} != "out-of-nothing"
170 . error
171 .endif
172 .if ${:U:S,,out-of-nothing,W} != ""
173 . error
174 .endif
175 .if ${:U:S,^,out-of-nothing,W} != "out-of-nothing"
176 . error
177 .endif
178 .if ${:U:S,$,out-of-nothing,W} != "out-of-nothing"
179 . error
180 .endif
181 .if ${:U:S,^$,out-of-nothing,W} != "out-of-nothing"
182 . error
183 .endif
184
185
186 mod-subst:
187 @echo $@:
188 @echo :${:Ua b b c:S,a b,,:Q}:
189 @echo :${:Ua b b c:S,a b,,1:Q}:
190 @echo :${:Ua b b c:S,a b,,W:Q}:
191 @echo :${:Ua b b c:S,b,,g:Q}:
192 @echo :${:U1 2 3 1 2 3:S,1 2,___,Wg:S,_,x,:Q}:
193 @echo ${:U12345:S,,sep,g:Q}
194
195 # The :S and :C modifiers accept an arbitrary character as the delimiter,
196 # including characters that are otherwise used as escape characters or
197 # interpreted in a special way. This can be used to confuse humans.
198 mod-subst-delimiter:
199 @echo $@:
200 @echo ${:U1 2 3:S 2 two :Q} horizontal tabulator
201 @echo ${:U1 2 3:S 2 two :Q} space
202 @echo ${:U1 2 3:S!2!two!:Q} exclamation mark
203 @echo ${:U1 2 3:S"2"two":Q} quotation mark
204 # In shell command lines, the hash does not need to be escaped.
205 # It needs to be escaped in variable assignment lines though.
206 @echo ${:U1 2 3:S#2#two#:Q} number sign
207 @echo ${:U1 2 3:S$2$two$:Q} dollar sign
208 @echo ${:U1 2 3:S%2%two%:Q} percent sign
209 @echo ${:U1 2 3:S&2&two&:Q} ampersand
210 @echo ${:U1 2 3:S'2'two':Q} apostrophe
211 @echo ${:U1 2 3:S(2(two(:Q} left parenthesis
212 @echo ${:U1 2 3:S)2)two):Q} right parenthesis
213 @echo ${:U1 2 3:S*2*two*:Q} asterisk
214 @echo ${:U1 2 3:S+2+two+:Q} plus sign
215 @echo ${:U1 2 3:S,2,two,:Q} comma
216 @echo ${:U1 2 3:S-2-two-:Q} hyphen-minus
217 @echo ${:U1 2 3:S.2.two.:Q} full stop
218 @echo ${:U1 2 3:S/2/two/:Q} solidus
219 @echo ${:U1 2 3:S121two1:Q} digit
220 @echo ${:U1 2 3:S:2:two::Q} colon
221 @echo ${:U1 2 3:S;2;two;:Q} semicolon
222 @echo ${:U1 2 3:S<2<two<:Q} less-than sign
223 @echo ${:U1 2 3:S=2=two=:Q} equals sign
224 @echo ${:U1 2 3:S>2>two>:Q} greater-than sign
225 @echo ${:U1 2 3:S?2?two?:Q} question mark
226 @echo ${:U1 2 3:S@2@two@:Q} commercial at
227 @echo ${:U1 2 3:SA2AtwoA:Q} capital letter
228 @echo ${:U1 2 3:S[2[two[:Q} left square bracket
229 @echo ${:U1 2 3:S\2\two\:Q} reverse solidus
230 @echo ${:U1 2 3:S]2]two]:Q} right square bracket
231 @echo ${:U1 2 3:S^2^two^:Q} circumflex accent
232 @echo ${:U1 2 3:S_2_two_:Q} low line
233 @echo ${:U1 2 3:S`2`two`:Q} grave accent
234 @echo ${:U1 2 3:Sa2atwoa:Q} small letter
235 @echo ${:U1 2 3:S{2{two{:Q} left curly bracket
236 @echo ${:U1 2 3:S|2|two|:Q} vertical line
237 @echo ${:U1 2 3:S}2}two}:Q} right curly bracket
238 @echo ${:U1 2 3:S~2~two~:Q} tilde
239
240 # The :S and :C modifiers can be chained without a separating ':'.
241 # This is not documented in the manual page.
242 # It works because ApplyModifier_Subst scans for the known modifiers g1W
243 # and then just returns to ApplyModifiers. There, the colon is optionally
244 # skipped (see the *st.next == ':' at the end of the loop).
245 #
246 # Most other modifiers cannot be chained since their parsers skip until
247 # the next ':' or '}' or ')'.
248 mod-subst-chain:
249 @echo $@:
250 @echo ${:Ua b c:S,a,A,S,b,B,}.
251 # There is no 'i' modifier for the :S or :C modifiers.
252 # The error message is "make: Unknown modifier 'i'", which is
253 # kind of correct, although it is mixing the terms for variable
254 # modifiers with the matching modifiers.
255 # expect: make: Unknown modifier ":i"
256 @echo ${:Uvalue:S,a,x,i}.
257
258 # No matter how many dollar signs there are, they all get merged
259 # into a single dollar by the :S modifier.
260 #
261 # As of 2020-08-09, this is because ParseModifierPart sees a '$' and
262 # calls Var_Parse to expand the variable. In all other places, the "$$"
263 # is handled outside of Var_Parse. Var_Parse therefore considers "$$"
264 # one of the "really stupid names", skips the first dollar, and parsing
265 # continues with the next character. This repeats for the other dollar
266 # signs, except the one before the delimiter. That one is handled by
267 # the code that optionally interprets the '$' as the end-anchor in the
268 # first part of the :S modifier. That code doesn't call Var_Parse but
269 # simply copies the dollar to the result.
270 mod-subst-dollar:
271 @echo $@:${:U1:S,^,$,:Q}:
272 @echo $@:${:U2:S,^,$$,:Q}:
273 @echo $@:${:U3:S,^,$$$,:Q}:
274 @echo $@:${:U4:S,^,$$$$,:Q}:
275 @echo $@:${:U5:S,^,$$$$$,:Q}:
276 @echo $@:${:U6:S,^,$$$$$$,:Q}:
277 @echo $@:${:U7:S,^,$$$$$$$,:Q}:
278 @echo $@:${:U8:S,^,$$$$$$$$,:Q}:
279 @echo $@:${:U40:S,^,$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$,:Q}:
280 # This generates no dollar at all:
281 @echo $@:${:UU8:S,^,${:U$$$$$$$$},:Q}:
282 # Here is an alternative way to generate dollar signs.
283 # It's unexpectedly complicated though.
284 @echo $@:${:U:range=5:ts\x24:C,[0-9],,g:Q}:
285 # In modifiers, dollars are escaped using the backslash, not using another
286 # dollar sign. Therefore, creating a dollar sign is pretty simple:
287 @echo $@:${:Ugood3:S,^,\$\$\$,:Q}
288