1# $NetBSD: cond-op.mk,v 1.10 2020/11/15 14:58:14 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 2020-09-11, that part of the condition is evaluated nevertheless, 65# since CondParser_Expr 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 101all: 102 @:; 103