cond-op.mk revision 1.16 1 # $NetBSD: cond-op.mk,v 1.16 2023/06/01 20:56:35 rillig Exp $
2 #
3 # Tests for operators like &&, ||, ! in .if conditions.
4 #
5 # See also:
6 # cond-op-and.mk
7 # cond-op-not.mk
8 # cond-op-or.mk
9 # cond-op-parentheses.mk
10
11 # In make, && binds more tightly than ||, like in C.
12 # If make had the same precedence for both && and ||, like in the shell,
13 # the result would be different.
14 # If || were to bind more tightly than &&, the result would be different
15 # as well.
16 .if !(1 || 1 && 0)
17 . error
18 .endif
19
20 # If make were to interpret the && and || operators like the shell, the
21 # previous condition would be interpreted as:
22 .if (1 || 1) && 0
23 . error
24 .endif
25
26 # The precedence of the ! operator is different from C though. It has a
27 # lower precedence than the comparison operators. Negating a condition
28 # does not need parentheses.
29 #
30 # This kind of condition looks so unfamiliar that it doesn't occur in
31 # practice.
32 .if !"word" == "word"
33 . error
34 .endif
35
36 # This is how the above condition is actually interpreted.
37 .if !("word" == "word")
38 . error
39 .endif
40
41 # TODO: Demonstrate that the precedence of the ! and == operators actually
42 # makes a difference. There is a simple example for sure, I just cannot
43 # wrap my head around it right now. See the truth table generator below
44 # for an example that doesn't require much thought.
45
46 # This condition is malformed because the '!' on the right-hand side must not
47 # appear unquoted. If any, it must be enclosed in quotes.
48 # In any case, it is not interpreted as a negation of an unquoted string.
49 # See CondParser_String.
50 # expect+1: Malformed conditional ("!word" == !word)
51 .if "!word" == !word
52 . error
53 .endif
54
55 # Surprisingly, the ampersand and pipe are allowed in bare strings.
56 # That's another opportunity for writing confusing code.
57 # See CondParser_String, which only has '!' in the list of stop characters.
58 .if "a&&b||c" != a&&b||c
59 . error
60 .endif
61
62 # In the following malformed conditions, as soon as the parser sees the '$'
63 # after the '0' or the '1', it knows that the condition will be malformed.
64 # Therefore there is no point in evaluating the misplaced expression.
65 #
66 # Before cond.c 1.286 from 2021-12-10, the extra expression was evaluated
67 # nevertheless, since CondParser_Or and CondParser_And asked for the expanded
68 # next token, even though in this position of the condition, only comparison
69 # operators, TOK_AND, TOK_OR or TOK_RPAREN are allowed.
70 .undef ERR
71 # expect+1: Malformed conditional (0 ${ERR::=evaluated})
72 .if 0 ${ERR::=evaluated}
73 . error
74 .endif
75 .if ${ERR:Uundefined} == undefined
76 # expect+1: A misplaced expression after 0 is not evaluated.
77 . info A misplaced expression after 0 is not evaluated.
78 .endif
79
80 .undef ERR
81 # expect+1: Malformed conditional (1 ${ERR::=evaluated})
82 .if 1 ${ERR::=evaluated}
83 . error
84 .endif
85 .if ${ERR:Uundefined} == undefined
86 # expect+1: A misplaced expression after 1 is not evaluated.
87 . info A misplaced expression after 1 is not evaluated.
88 .endif
89
90
91 # Demonstration that '&&' has higher precedence than '||'.
92 # expect+1: A B C => (A || B) && C A || B && C A || (B && C)
93 .info A B C => (A || B) && C A || B && C A || (B && C)
94 .for a in 0 1
95 . for b in 0 1
96 . for c in 0 1
97 . for r1 in ${ ($a || $b) && $c :?1:0}
98 . for r2 in ${ $a || $b && $c :?1:0}
99 . for r3 in ${ $a || ($b && $c) :?1:0}
100 # expect+8: 0 0 0 => 0 0 0
101 # expect+7: 0 0 1 => 0 0 0
102 # expect+6: 0 1 0 => 0 0 0
103 # expect+5: 0 1 1 => 1 1 1
104 # expect+4: 1 0 0 => 0 1 1
105 # expect+3: 1 0 1 => 1 1 1
106 # expect+2: 1 1 0 => 0 1 1
107 # expect+1: 1 1 1 => 1 1 1
108 . info $a $b $c => ${r1} ${r2} ${r3}
109 . endfor
110 . endfor
111 . endfor
112 . endfor
113 . endfor
114 .endfor
115
116 # This condition is obviously malformed. It is properly detected and also
117 # was properly detected before 2021-01-19, but only because the left hand
118 # side of the '&&' evaluated to true.
119 # expect+1: Malformed conditional (1 &&)
120 .if 1 &&
121 . error
122 .else
123 . error
124 .endif
125
126 # This obviously malformed condition was not detected as such before cond.c
127 # 1.238 from 2021-01-19.
128 # expect+1: Malformed conditional (0 &&)
129 .if 0 &&
130 . error
131 .else
132 . error
133 .endif
134
135 # This obviously malformed condition was not detected as such before cond.c
136 # 1.238 from 2021-01-19.
137 # expect+1: Malformed conditional (1 ||)
138 .if 1 ||
139 . error
140 .else
141 . error
142 .endif
143
144 # This condition is obviously malformed. It is properly detected and also
145 # was properly detected before 2021-01-19, but only because the left hand
146 # side of the '||' evaluated to false.
147 # expect+1: Malformed conditional (0 ||)
148 .if 0 ||
149 . error
150 .else
151 . error
152 .endif
153
154 all:
155 @:;
156