summaryrefslogtreecommitdiff
path: root/usr.bin/make/unit-tests/cond-op.mk
blob: 6371e4db0594ddf8b058581035981c12f5c4e4d5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
# $NetBSD: cond-op.mk,v 1.16 2023/06/01 20:56:35 rillig Exp $
#
# Tests for operators like &&, ||, ! in .if conditions.
#
# See also:
#	cond-op-and.mk
#	cond-op-not.mk
#	cond-op-or.mk
#	cond-op-parentheses.mk

# In make, && binds more tightly than ||, like in C.
# If make had the same precedence for both && and ||, like in the shell,
# the result would be different.
# If || were to bind more tightly than &&, the result would be different
# as well.
.if !(1 || 1 && 0)
.  error
.endif

# If make were to interpret the && and || operators like the shell, the
# previous condition would be interpreted as:
.if (1 || 1) && 0
.  error
.endif

# The precedence of the ! operator is different from C though. It has a
# lower precedence than the comparison operators.  Negating a condition
# does not need parentheses.
#
# This kind of condition looks so unfamiliar that it doesn't occur in
# practice.
.if !"word" == "word"
.  error
.endif

# This is how the above condition is actually interpreted.
.if !("word" == "word")
.  error
.endif

# TODO: Demonstrate that the precedence of the ! and == operators actually
# makes a difference.  There is a simple example for sure, I just cannot
# wrap my head around it right now.  See the truth table generator below
# for an example that doesn't require much thought.

# This condition is malformed because the '!' on the right-hand side must not
# appear unquoted.  If any, it must be enclosed in quotes.
# In any case, it is not interpreted as a negation of an unquoted string.
# See CondParser_String.
# expect+1: Malformed conditional ("!word" == !word)
.if "!word" == !word
.  error
.endif

# Surprisingly, the ampersand and pipe are allowed in bare strings.
# That's another opportunity for writing confusing code.
# See CondParser_String, which only has '!' in the list of stop characters.
.if "a&&b||c" != a&&b||c
.  error
.endif

# In the following malformed conditions, as soon as the parser sees the '$'
# after the '0' or the '1', it knows that the condition will be malformed.
# Therefore there is no point in evaluating the misplaced expression.
#
# Before cond.c 1.286 from 2021-12-10, the extra expression was evaluated
# nevertheless, since CondParser_Or and CondParser_And asked for the expanded
# next token, even though in this position of the condition, only comparison
# operators, TOK_AND, TOK_OR or TOK_RPAREN are allowed.
.undef ERR
# expect+1: Malformed conditional (0 ${ERR::=evaluated})
.if 0 ${ERR::=evaluated}
.  error
.endif
.if ${ERR:Uundefined} == undefined
# expect+1: A misplaced expression after 0 is not evaluated.
.  info A misplaced expression after 0 is not evaluated.
.endif

.undef ERR
# expect+1: Malformed conditional (1 ${ERR::=evaluated})
.if 1 ${ERR::=evaluated}
.  error
.endif
.if ${ERR:Uundefined} == undefined
# expect+1: A misplaced expression after 1 is not evaluated.
.  info A misplaced expression after 1 is not evaluated.
.endif


# Demonstration that '&&' has higher precedence than '||'.
# expect+1: A B C   =>   (A || B) && C   A || B && C   A || (B && C)
.info A B C   =>   (A || B) && C   A || B && C   A || (B && C)
.for a in 0 1
.  for b in 0 1
.    for c in 0 1
.      for r1 in ${ ($a || $b) && $c :?1:0}
.        for r2 in ${ $a || $b && $c :?1:0}
.          for r3 in ${ $a || ($b && $c) :?1:0}
# expect+8: 0 0 0   =>   0               0             0
# expect+7: 0 0 1   =>   0               0             0
# expect+6: 0 1 0   =>   0               0             0
# expect+5: 0 1 1   =>   1               1             1
# expect+4: 1 0 0   =>   0               1             1
# expect+3: 1 0 1   =>   1               1             1
# expect+2: 1 1 0   =>   0               1             1
# expect+1: 1 1 1   =>   1               1             1
.            info $a $b $c   =>   ${r1}               ${r2}             ${r3}
.          endfor
.        endfor
.      endfor
.    endfor
.  endfor
.endfor

# This condition is obviously malformed.  It is properly detected and also
# was properly detected before 2021-01-19, but only because the left hand
# side of the '&&' evaluated to true.
# expect+1: Malformed conditional (1 &&)
.if 1 &&
.  error
.else
.  error
.endif

# This obviously malformed condition was not detected as such before cond.c
# 1.238 from 2021-01-19.
# expect+1: Malformed conditional (0 &&)
.if 0 &&
.  error
.else
.  error
.endif

# This obviously malformed condition was not detected as such before cond.c
# 1.238 from 2021-01-19.
# expect+1: Malformed conditional (1 ||)
.if 1 ||
.  error
.else
.  error
.endif

# This condition is obviously malformed.  It is properly detected and also
# was properly detected before 2021-01-19, but only because the left hand
# side of the '||' evaluated to false.
# expect+1: Malformed conditional (0 ||)
.if 0 ||
.  error
.else
.  error
.endif

all:
	@:;