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