xref: /freebsd/contrib/bmake/unit-tests/varmod-subst.mk (revision 96190b4fef3b4a0cc3ca0606b0c4e3e69a5e6717)
1# $NetBSD: varmod-subst.mk,v 1.14 2023/12/18 11:13:51 rillig Exp $
2#
3# Tests for the :S,from,to, variable modifier.
4
5all: mod-subst
6all: mod-subst-delimiter
7all: mod-subst-chain
8all: mod-subst-dollar
9
10WORDS=		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
186mod-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.
198mod-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 ')'.
248mod-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	@echo ${:Uvalue:S,a,x,i}.
256
257# No matter how many dollar signs there are, they all get merged
258# into a single dollar by the :S modifier.
259#
260# As of 2020-08-09, this is because ParseModifierPart sees a '$' and
261# calls Var_Parse to expand the variable.  In all other places, the "$$"
262# is handled outside of Var_Parse.  Var_Parse therefore considers "$$"
263# one of the "really stupid names", skips the first dollar, and parsing
264# continues with the next character.  This repeats for the other dollar
265# signs, except the one before the delimiter.  That one is handled by
266# the code that optionally interprets the '$' as the end-anchor in the
267# first part of the :S modifier.  That code doesn't call Var_Parse but
268# simply copies the dollar to the result.
269mod-subst-dollar:
270	@echo $@:${:U1:S,^,$,:Q}:
271	@echo $@:${:U2:S,^,$$,:Q}:
272	@echo $@:${:U3:S,^,$$$,:Q}:
273	@echo $@:${:U4:S,^,$$$$,:Q}:
274	@echo $@:${:U5:S,^,$$$$$,:Q}:
275	@echo $@:${:U6:S,^,$$$$$$,:Q}:
276	@echo $@:${:U7:S,^,$$$$$$$,:Q}:
277	@echo $@:${:U8:S,^,$$$$$$$$,:Q}:
278	@echo $@:${:U40:S,^,$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$,:Q}:
279# This generates no dollar at all:
280	@echo $@:${:UU8:S,^,${:U$$$$$$$$},:Q}:
281# Here is an alternative way to generate dollar signs.
282# It's unexpectedly complicated though.
283	@echo $@:${:U:range=5:ts\x24:C,[0-9],,g:Q}:
284# In modifiers, dollars are escaped using the backslash, not using another
285# dollar sign.  Therefore, creating a dollar sign is pretty simple:
286	@echo $@:${:Ugood3:S,^,\$\$\$,:Q}
287