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