Home | History | Annotate | Line # | Download | only in unit-tests
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