xref: /freebsd/contrib/bmake/unit-tests/cond-op.mk (revision 911f0260390e18cf85f3dbf2c719b593efdc1e3c)
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
154all:
155	@:;
156