1# $NetBSD: cond-op.mk,v 1.13 2021/01/19 18:20:30 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.if "!word" == !word
51.  error
52.endif
53
54# Surprisingly, the ampersand and pipe are allowed in bare strings.
55# That's another opportunity for writing confusing code.
56# See CondParser_String, which only has '!' in the list of stop characters.
57.if "a&&b||c" != a&&b||c
58.  error
59.endif
60
61# As soon as the parser sees the '$', it knows that the condition will
62# be malformed.  Therefore there is no point in evaluating it.
63#
64# As of 2021-01-20, that part of the condition is evaluated nevertheless,
65# since CondParser_Or just requests the next token, without restricting
66# the token to the expected tokens.  If the parser were to restrict the
67# valid follow tokens for the token "0" to those that can actually produce
68# a correct condition (which in this case would be comparison operators,
69# TOK_AND, TOK_OR or TOK_RPAREN), the variable expression would not have
70# to be evaluated.
71#
72# This would add a good deal of complexity to the code though, for almost
73# no benefit, especially since most expressions and conditions are side
74# effect free.
75.if 0 ${ERR::=evaluated}
76.  error
77.endif
78.if ${ERR:Uundefined} == evaluated
79.  info After detecting a parse error, the rest is evaluated.
80.endif
81
82# Just in case that parsing should ever stop on the first error.
83.info Parsing continues until here.
84
85# Demonstration that '&&' has higher precedence than '||'.
86.info A B C   =>   (A || B) && C   A || B && C   A || (B && C)
87.for a in 0 1
88.  for b in 0 1
89.    for c in 0 1
90.      for r1 in ${ ($a || $b) && $c :?1:0}
91.        for r2 in ${ $a || $b && $c :?1:0}
92.          for r3 in ${ $a || ($b && $c) :?1:0}
93.            info $a $b $c   =>   ${r1}               ${r2}             ${r3}
94.          endfor
95.        endfor
96.      endfor
97.    endfor
98.  endfor
99.endfor
100
101# This condition is obviously malformed.  It is properly detected and also
102# was properly detected before 2021-01-19, but only because the left hand
103# side of the '&&' evaluated to true.
104.if 1 &&
105.  error
106.else
107.  error
108.endif
109
110# This obviously malformed condition was not detected as such before cond.c
111# 1.238 from 2021-01-19.
112.if 0 &&
113.  error
114.else
115.  error
116.endif
117
118# This obviously malformed condition was not detected as such before cond.c
119# 1.238 from 2021-01-19.
120.if 1 ||
121.  error
122.else
123.  error
124.endif
125
126# This condition is obviously malformed.  It is properly detected and also
127# was properly detected before 2021-01-19, but only because the left hand
128# side of the '||' evaluated to false.
129.if 0 ||
130.  error
131.else
132.  error
133.endif
134
135all:
136	@:;
137