varmod-match.mk revision 1.9 1 # $NetBSD: varmod-match.mk,v 1.9 2022/05/08 06:51:27 rillig Exp $
2 #
3 # Tests for the :M variable modifier, which filters words that match the
4 # given pattern.
5 #
6 # See ApplyModifier_Match and ModifyWord_Match for the implementation.
7
8 .MAKEFLAGS: -dc
9
10 NUMBERS= One Two Three Four five six seven
11
12 # Only keep words that start with an uppercase letter.
13 .if ${NUMBERS:M[A-Z]*} != "One Two Three Four"
14 . error
15 .endif
16
17 # Only keep words that start with a character other than an uppercase letter.
18 .if ${NUMBERS:M[^A-Z]*} != "five six seven"
19 . error
20 .endif
21
22 # Only keep words that don't start with s and at the same time end with
23 # either of [ex].
24 #
25 # This test case ensures that the negation from the first character class
26 # does not propagate to the second character class.
27 .if ${NUMBERS:M[^s]*[ex]} != "One Three five"
28 . error
29 .endif
30
31 # Before 2020-06-13, this expression took quite a long time in Str_Match,
32 # calling itself 601080390 times for 16 asterisks.
33 .if ${:U****************:M****************b}
34 .endif
35
36 # To match a dollar sign in a word, double it.
37 #
38 # This is different from the :S and :C variable modifiers, where a '$'
39 # has to be escaped as '\$'.
40 .if ${:Ua \$ sign:M*$$*} != "\$"
41 . error
42 .endif
43
44 # In the :M modifier, '\$' does not escape a dollar. Instead it is
45 # interpreted as a backslash followed by whatever expression the
46 # '$' starts.
47 #
48 # This differs from the :S, :C and several other variable modifiers.
49 ${:U*}= asterisk
50 .if ${:Ua \$ sign any-asterisk:M*\$*} != "any-asterisk"
51 . error
52 .endif
53
54 # TODO: ${VAR:M(((}}}}
55 # TODO: ${VAR:M{{{)))}
56 # TODO: ${VAR:M${UNBALANCED}}
57 # TODO: ${VAR:M${:U(((\}\}\}}}
58
59 .MAKEFLAGS: -d0
60
61 # Special characters:
62 # * matches 0 or more arbitrary characters
63 # ? matches a single arbitrary character
64 # \ starts an escape sequence, only outside ranges
65 # [ starts a set for matching a single character
66 # ] ends a set for matching a single character
67 # - in a set, forms a range of characters
68 # ^ as the first character in a set, negates the set
69 # ( during parsing of the pattern, starts a nesting level
70 # ) during parsing of the pattern, ends a nesting level
71 # { during parsing of the pattern, starts a nesting level
72 # } during parsing of the pattern, ends a nesting level
73 # : during parsing of the pattern, finishes the pattern
74 # $ during parsing of the pattern, starts a nested expression
75 # # in a line except a shell command, starts a comment
76 #
77 # Pattern parts:
78 # * matches 0 or more arbitrary characters
79 # ? matches exactly 1 arbitrary character
80 # \x matches exactly the character 'x'
81 # [...] matches exactly 1 character from the set
82 # [^...] matches exactly 1 character outside the set
83 # [a-z] matches exactly 1 character from the range 'a' to 'z'
84 #
85
86 # [] matches never
87 .if ${ ab a[]b a[b a b :L:M[]} != ""
88 . error
89 .endif
90
91 # a[]b matches never
92 .if ${ ab a[]b a[b a b [ ] :L:Ma[]b} != ""
93 . error
94 .endif
95
96 # [^] matches exactly 1 arbitrary character
97 .if ${ ab a[]b a[b a b [ ] :L:M[^]} != "a b [ ]"
98 . error
99 .endif
100
101 # a[^]b matches 'a', then exactly 1 arbitrary character, then 'b'
102 .if ${ ab a[]b a[b a b :L:Ma[^]b} != "a[b"
103 . error
104 .endif
105
106 # [Nn0] matches exactly 1 character from the set 'N', 'n', '0'
107 .if ${ a b N n 0 Nn0 [ ] :L:M[Nn0]} != "N n 0"
108 . error
109 .endif
110
111 # [a-c] matches exactly 1 character from the range 'a' to 'c'
112 .if ${ A B C a b c d [a-c] [a] :L:M[a-c]} != "a b c"
113 . error
114 .endif
115
116 # [c-a] matches the same as [a-c]
117 .if ${ A B C a b c d [a-c] [a] :L:M[c-a]} != "a b c"
118 . error
119 .endif
120
121 # [^a-c67]
122 # matches a single character, except for 'a', 'b', 'c', '6' or
123 # '7'
124 .if ${ A B C a b c d 5 6 7 8 [a-c] [a] :L:M[^a-c67]} != "A B C d 5 8"
125 . error
126 .endif
127
128 # : terminates the pattern
129 .if ${ A * :L:M:} != ""
130 . error
131 .endif
132
133 # \: matches a colon
134 .if ${ ${:U\: \:\:} :L:M\:} != ":"
135 . error
136 .endif
137
138 # ${:U\:} matches a colon
139 .if ${ ${:U\:} ${:U\:\:} :L:M${:U\:}} != ":"
140 . error
141 .endif
142
143 # [:] matches never since the ':' starts the next modifier
144 # expect+2: Unknown modifier "]"
145 # expect+1: Malformed conditional (${ ${:U\:} ${:U\:\:} :L:M[:]} != ":")
146 .if ${ ${:U\:} ${:U\:\:} :L:M[:]} != ":"
147 . error
148 .else
149 . error
150 .endif
151
152 # [\] matches exactly a backslash; no escaping takes place in
153 # character ranges
154 # Without the 'a' in the below expressions, the backslash would end a word and
155 # thus influence how the string is split into words.
156 .if ${ ${:U\\a} ${:U\\\\a} :L:M[\]a} != "\\a"
157 . error
158 .endif
159
160 #.MAKEFLAGS: -dcv
161 #
162 # Incomplete patterns:
163 # [ matches TODO
164 # [x matches TODO
165 # [^ matches TODO
166 # [- matches TODO
167 # [xy matches TODO
168 # [^x matches TODO
169 # [\ matches TODO
170 #
171 # [x- matches exactly 'x', doesn't match 'x-'
172 # [^x- matches TODO
173 # \ matches never
174
175
176 # The modifier ':tW' prevents splitting at whitespace. Even leading and
177 # trailing whitespace is preserved.
178 .if ${ plain string :L:tW:M*} != " plain string "
179 . error
180 .endif
181
182 # Without the modifier ':tW', the string is split into words. All whitespace
183 # around and between the words is normalized to a single space.
184 .if ${ plain string :L:M*} != "plain string"
185 . error
186 .endif
187
188
189 # The pattern can come from a variable expression. For single-letter
190 # variables, either the short form or the long form can be used, just as
191 # everywhere else.
192 PRIMES= 2 3 5 7 11
193 n= 2
194 .if ${PRIMES:M$n} != "2"
195 . error
196 .endif
197 .if ${PRIMES:M${n}} != "2"
198 . error
199 .endif
200 .if ${PRIMES:M${:U2}} != "2"
201 . error
202 .endif
203