varmod-indirect.mk revision 1.11 1 1.11 rillig # $NetBSD: varmod-indirect.mk,v 1.11 2022/01/15 12:35:18 rillig Exp $
2 1.1 rillig #
3 1.1 rillig # Tests for indirect variable modifiers, such as in ${VAR:${M_modifiers}}.
4 1.1 rillig # These can be used for very basic purposes like converting a string to either
5 1.1 rillig # uppercase or lowercase, as well as for fairly advanced modifiers that first
6 1.1 rillig # look like line noise and are hard to decipher.
7 1.1 rillig #
8 1.7 rillig # Initial support for indirect modifiers was added in var.c 1.101 from
9 1.7 rillig # 2006-02-18. Since var.c 1.108 from 2006-05-11 it is possible to use
10 1.7 rillig # indirect modifiers for all but the very first modifier as well.
11 1.1 rillig
12 1.1 rillig
13 1.2 rillig # To apply a modifier indirectly via another variable, the whole
14 1.2 rillig # modifier must be put into a single variable expression.
15 1.6 rillig # The following expression generates a parse error since its indirect
16 1.6 rillig # modifier contains more than a sole variable expression.
17 1.6 rillig #
18 1.11 rillig # expect+1: Unknown modifier "${"
19 1.2 rillig .if ${value:L:${:US}${:U,value,replacement,}} != "S,value,replacement,}"
20 1.2 rillig . warning unexpected
21 1.2 rillig .endif
22 1.2 rillig
23 1.2 rillig
24 1.2 rillig # Adding another level of indirection (the 2 nested :U expressions) helps.
25 1.2 rillig .if ${value:L:${:U${:US}${:U,value,replacement,}}} != "replacement"
26 1.2 rillig . warning unexpected
27 1.2 rillig .endif
28 1.2 rillig
29 1.2 rillig
30 1.2 rillig # Multiple indirect modifiers can be applied one after another as long as
31 1.2 rillig # they are separated with colons.
32 1.2 rillig .if ${value:L:${:US,a,A,}:${:US,e,E,}} != "vAluE"
33 1.2 rillig . warning unexpected
34 1.2 rillig .endif
35 1.2 rillig
36 1.2 rillig
37 1.6 rillig # An indirect variable that evaluates to the empty string is allowed.
38 1.6 rillig # It is even allowed to write another modifier directly afterwards.
39 1.6 rillig # There is no practical use case for this feature though, as demonstrated
40 1.6 rillig # in the test case directly below.
41 1.6 rillig .if ${value:L:${:Dempty}S,value,replaced,} != "replaced"
42 1.6 rillig . warning unexpected
43 1.6 rillig .endif
44 1.6 rillig
45 1.6 rillig # If an expression for an indirect modifier evaluates to anything else than an
46 1.6 rillig # empty string and is neither followed by a ':' nor '}', this produces a parse
47 1.6 rillig # error. Because of this parse error, this feature cannot be used reasonably
48 1.6 rillig # in practice.
49 1.6 rillig #
50 1.11 rillig # expect+2: Unknown modifier "${"
51 1.6 rillig #.MAKEFLAGS: -dvc
52 1.6 rillig .if ${value:L:${:UM*}S,value,replaced,} == "M*S,value,replaced,}"
53 1.6 rillig . warning FIXME: this expression should have resulted in a parse $\
54 1.6 rillig error rather than returning the unparsed portion of the $\
55 1.6 rillig expression.
56 1.6 rillig .else
57 1.6 rillig . error
58 1.6 rillig .endif
59 1.6 rillig #.MAKEFLAGS: -d0
60 1.6 rillig
61 1.6 rillig # An indirect modifier can be followed by other modifiers, no matter if the
62 1.6 rillig # indirect modifier evaluates to an empty string or not.
63 1.6 rillig #
64 1.2 rillig # This makes it possible to define conditional modifiers, like this:
65 1.2 rillig #
66 1.2 rillig # M.little-endian= S,1234,4321,
67 1.2 rillig # M.big-endian= # none
68 1.6 rillig .if ${value:L:${:D empty }:S,value,replaced,} != "replaced"
69 1.6 rillig . error
70 1.2 rillig .endif
71 1.2 rillig
72 1.2 rillig
73 1.1 rillig # The nested variable expression expands to "tu", and this is interpreted as
74 1.1 rillig # a variable modifier for the value "Upper", resulting in "UPPER".
75 1.1 rillig .if ${Upper:L:${:Utu}} != "UPPER"
76 1.1 rillig . error
77 1.1 rillig .endif
78 1.1 rillig
79 1.1 rillig # The nested variable expression expands to "tl", and this is interpreted as
80 1.1 rillig # a variable modifier for the value "Lower", resulting in "lower".
81 1.1 rillig .if ${Lower:L:${:Utl}} != "lower"
82 1.1 rillig . error
83 1.1 rillig .endif
84 1.1 rillig
85 1.1 rillig
86 1.1 rillig # The nested variable expression is ${1 != 1:?Z:tl}, consisting of the
87 1.1 rillig # condition "1 != 1", the then-branch "Z" and the else-branch "tl". Since
88 1.1 rillig # the condition evaluates to false, the then-branch is ignored (it would
89 1.1 rillig # have been an unknown modifier anyway) and the ":tl" modifier is applied.
90 1.1 rillig .if ${Mixed:L:${1 != 1:?Z:tl}} != "mixed"
91 1.1 rillig . error
92 1.1 rillig .endif
93 1.1 rillig
94 1.1 rillig
95 1.1 rillig # The indirect modifier can also replace an ':L' modifier, which allows for
96 1.1 rillig # brain twisters since by reading the expression alone, it is not possible
97 1.1 rillig # to say whether the variable name will be evaluated as a variable name or
98 1.1 rillig # as the immediate value of the expression.
99 1.1 rillig VAR= value
100 1.1 rillig M_ExpandVar= # an empty modifier
101 1.1 rillig M_VarAsValue= L
102 1.1 rillig #
103 1.1 rillig .if ${VAR:${M_ExpandVar}} != "value"
104 1.1 rillig . error
105 1.1 rillig .endif
106 1.1 rillig .if ${VAR:${M_VarAsValue}} != "VAR"
107 1.1 rillig . error
108 1.1 rillig .endif
109 1.1 rillig
110 1.1 rillig # The indirect modifier M_ListToSkip, when applied to a list of patterns,
111 1.1 rillig # expands to a sequence of ':N' modifiers, each of which filters one of the
112 1.1 rillig # patterns. This list of patterns can then be applied to another variable
113 1.1 rillig # to actually filter that variable.
114 1.1 rillig #
115 1.1 rillig M_ListToSkip= @pat@N$${pat}@:ts:
116 1.1 rillig #
117 1.1 rillig # The dollar signs need to be doubled in the above modifier expression,
118 1.1 rillig # otherwise they would be expanded too early, that is, when parsing the
119 1.1 rillig # modifier itself.
120 1.1 rillig #
121 1.1 rillig # In the following example, M_NoPrimes expands to 'N2:N3:N5:N7:N1[1379]'.
122 1.1 rillig # The 'N' comes from the expression 'N${pat}', the separating colons come
123 1.1 rillig # from the modifier ':ts:'.
124 1.1 rillig #
125 1.1 rillig #.MAKEFLAGS: -dcv # Uncomment this line to see the details
126 1.1 rillig #
127 1.1 rillig PRIMES= 2 3 5 7 1[1379]
128 1.1 rillig M_NoPrimes= ${PRIMES:${M_ListToSkip}}
129 1.1 rillig .if ${:U:range=20:${M_NoPrimes}} != "1 4 6 8 9 10 12 14 15 16 18 20"
130 1.1 rillig . error
131 1.1 rillig .endif
132 1.1 rillig .MAKEFLAGS: -d0
133 1.1 rillig
134 1.3 rillig
135 1.3 rillig # In contrast to the .if conditions, the .for loop allows undefined variable
136 1.3 rillig # expressions. These expressions expand to empty strings.
137 1.4 rillig
138 1.4 rillig # An undefined expression without any modifiers expands to an empty string.
139 1.4 rillig .for var in before ${UNDEF} after
140 1.4 rillig . info ${var}
141 1.4 rillig .endfor
142 1.4 rillig
143 1.4 rillig # An undefined expression with only modifiers that keep the expression
144 1.4 rillig # undefined expands to an empty string.
145 1.4 rillig .for var in before ${UNDEF:${:US,a,a,}} after
146 1.3 rillig . info ${var}
147 1.3 rillig .endfor
148 1.3 rillig
149 1.3 rillig # Even in an indirect modifier based on an undefined variable, the value of
150 1.3 rillig # the expression in Var_Parse is a simple empty string.
151 1.4 rillig .for var in before ${UNDEF:${:U}} after
152 1.4 rillig . info ${var}
153 1.4 rillig .endfor
154 1.4 rillig
155 1.4 rillig # An error in an indirect modifier.
156 1.4 rillig .for var in before ${UNDEF:${:UZ}} after
157 1.3 rillig . info ${var}
158 1.3 rillig .endfor
159 1.3 rillig
160 1.4 rillig
161 1.4 rillig # Another slightly different evaluation context is the right-hand side of
162 1.4 rillig # a variable assignment using ':='.
163 1.4 rillig .MAKEFLAGS: -dpv
164 1.5 rillig
165 1.4 rillig # The undefined variable expression is kept as-is.
166 1.4 rillig _:= before ${UNDEF} after
167 1.5 rillig
168 1.4 rillig # The undefined variable expression is kept as-is.
169 1.4 rillig _:= before ${UNDEF:${:US,a,a,}} after
170 1.5 rillig
171 1.4 rillig # XXX: The subexpression ${:U} is fully defined, therefore it is expanded.
172 1.4 rillig # This results in ${UNDEF:}, which can lead to tricky parse errors later,
173 1.4 rillig # when the variable '_' is expanded further.
174 1.5 rillig #
175 1.4 rillig # XXX: What should be the correct strategy here? One possibility is to
176 1.5 rillig # expand the defined subexpression and replace it with ${:U...}, just like
177 1.4 rillig # in .for loops. This would preserve the structure of the expression while
178 1.4 rillig # at the same time expanding the expression as far as possible.
179 1.4 rillig _:= before ${UNDEF:${:U}} after
180 1.5 rillig
181 1.4 rillig # XXX: This expands to ${UNDEF:Z}, which will behave differently if the
182 1.4 rillig # variable '_' is used in a context where the variable expression ${_} is
183 1.4 rillig # parsed but not evaluated.
184 1.4 rillig _:= before ${UNDEF:${:UZ}} after
185 1.5 rillig
186 1.4 rillig .MAKEFLAGS: -d0
187 1.4 rillig .undef _
188 1.4 rillig
189 1.6 rillig
190 1.6 rillig # When evaluating indirect modifiers, these modifiers may expand to ':tW',
191 1.6 rillig # which modifies the interpretation of the expression value. This modified
192 1.6 rillig # interpretation only lasts until the end of the indirect modifier, it does
193 1.6 rillig # not influence the outer variable expression.
194 1.6 rillig .if ${1 2 3:L:tW:[#]} != 1 # direct :tW applies to the :[#]
195 1.6 rillig . error
196 1.6 rillig .endif
197 1.6 rillig .if ${1 2 3:L:${:UtW}:[#]} != 3 # indirect :tW does not apply to :[#]
198 1.6 rillig . error
199 1.6 rillig .endif
200 1.6 rillig
201 1.6 rillig
202 1.6 rillig # When evaluating indirect modifiers, these modifiers may expand to ':ts*',
203 1.6 rillig # which modifies the interpretation of the expression value. This modified
204 1.6 rillig # interpretation only lasts until the end of the indirect modifier, it does
205 1.6 rillig # not influence the outer variable expression.
206 1.6 rillig #
207 1.7 rillig # In this first expression, the direct ':ts*' has no effect since ':U' does not
208 1.6 rillig # treat the expression value as a list of words but as a single word. It has
209 1.6 rillig # to be ':U', not ':D', since the "expression name" is "1 2 3" and there is no
210 1.6 rillig # variable of that name.
211 1.6 rillig #.MAKEFLAGS: -dcpv
212 1.6 rillig .if ${1 2 3:L:ts*:Ua b c} != "a b c"
213 1.6 rillig . error
214 1.6 rillig .endif
215 1.6 rillig # In this expression, the direct ':ts*' affects the ':M' at the end.
216 1.6 rillig .if ${1 2 3:L:ts*:Ua b c:M*} != "a*b*c"
217 1.6 rillig . error
218 1.6 rillig .endif
219 1.6 rillig # In this expression, the ':ts*' is indirect, therefore the changed separator
220 1.8 rillig # only applies to the modifiers from the indirect text. It does not affect
221 1.8 rillig # the ':M' since that is not part of the text from the indirect modifier.
222 1.8 rillig #
223 1.8 rillig # Implementation detail: when ApplyModifiersIndirect calls ApplyModifiers
224 1.9 rillig # (which creates a new ModChain containing a fresh separator),
225 1.8 rillig # the outer separator character is not passed by reference to the inner
226 1.8 rillig # evaluation, therefore the scope of the inner separator ends after applying
227 1.8 rillig # the modifier ':ts*'.
228 1.6 rillig .if ${1 2 3:L:${:Uts*}:Ua b c:M*} != "a b c"
229 1.6 rillig . error
230 1.6 rillig .endif
231 1.6 rillig
232 1.8 rillig # A direct modifier ':U' turns the expression from undefined to defined.
233 1.8 rillig # An indirect modifier ':U' has the same effect, unlike the separator from
234 1.8 rillig # ':ts*' or the single-word marker from ':tW'.
235 1.8 rillig #
236 1.8 rillig # This is because when ApplyModifiersIndirect calls ApplyModifiers, it passes
237 1.8 rillig # the definedness of the outer expression by reference. If that weren't the
238 1.8 rillig # case, the first condition below would result in a parse error because its
239 1.8 rillig # left-hand side would be undefined.
240 1.8 rillig .if ${UNDEF:${:UUindirect-fallback}} != "indirect-fallback"
241 1.8 rillig . error
242 1.8 rillig .endif
243 1.8 rillig .if ${UNDEF:${:UUindirect-fallback}:Uouter-fallback} != "outer-fallback"
244 1.8 rillig . error
245 1.8 rillig .endif
246 1.8 rillig
247 1.1 rillig all:
248