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