1261046Smav# $NetBSD: cond-token-plain.mk,v 1.19 2023/11/19 21:47:52 rillig Exp $ 274462Salfred# 374462Salfred# Tests for plain tokens (that is, string literals without quotes) 4261046Smav# in .if conditions. These are also called bare words. 5261046Smav 674462Salfred.MAKEFLAGS: -dc 7261046Smav 8261046Smav# The word 'value' after the '!=' is a bare word. 9261046Smav.if ${:Uvalue} != value 10261046Smav. error 11261046Smav.endif 12261046Smav 13261046Smav# Using a '#' in a string literal in a condition leads to a malformed 14261046Smav# condition since comment parsing is done in an early phase and removes the 15261046Smav# '#' and everything after it long before the condition parser gets to see it. 16261046Smav# 1774462Salfred# XXX: The error message is missing for this malformed condition. 18261046Smav# The right-hand side of the comparison is just a '"', before unescaping. 19261046Smav.if ${:U} != "#hash" 20261046Smav. error 21261046Smav.endif 22261046Smav 23261046Smav# To get a '#' into a condition, it has to be escaped using a backslash. 24261046Smav# This prevents the comment parser from removing it, and in turn, it becomes 25261046Smav# visible to CondParser_String. 26261046Smav.if ${:U\#hash} != "\#hash" 27261046Smav. error 28261046Smav.endif 2974462Salfred 3074462Salfred# Since 2002-12-30, and still as of 2020-09-11, CondParser_Token handles 3174462Salfred# the '#' specially, even though at this point, there should be no need for 3274462Salfred# comment handling anymore. The comments are supposed to be stripped off 3374462Salfred# in a very early parsing phase. 3474462Salfred# 3574462Salfred# See https://gnats.netbsd.org/19596 for example makefiles demonstrating the 3674462Salfred# original problems. At that time, the parser didn't recognize the comment in 3774462Salfred# the line '.else # comment3'. This workaround is not needed anymore since 3874462Salfred# comments are stripped in an earlier phase. See "case '#'" in 3974462Salfred# CondParser_Token. 4074462Salfred# 4174462Salfred# XXX: Missing error message for the malformed condition. The right-hand 4274462Salfred# side before unescaping is double-quotes, backslash, backslash. 4374462Salfred.if ${:U\\} != "\\#hash" 4474462Salfred. error 4574462Salfred.endif 4674462Salfred 4774462Salfred# The right-hand side of a comparison is not parsed as a token, therefore 4874462Salfred# the code from CondParser_Token does not apply to it. 4974462Salfred# TODO: Explain the consequences. 5074462Salfred# TODO: Does this mean that more syntactic variants are allowed here? 5174462Salfred.if ${:U\#hash} != \#hash 5274462Salfred. error 5374462Salfred.endif 5474462Salfred 5574462Salfred# XXX: What is the purpose of treating an escaped '#' in the following 5674462Salfred# condition as a comment? And why only at the beginning of a token, 5774462Salfred# just as in the shell? 5874462Salfred.if 0 \# This is treated as a comment, but why? 5974462Salfred. error 6074462Salfred.endif 6174462Salfred 6274462Salfred# Ah, ok, this can be used to add an end-of-condition comment. But does 6374462Salfred# anybody really use this? This is neither documented nor obvious since 6474462Salfred# the '#' is escaped. It's much clearer to write a comment in the line 6574462Salfred# above the condition. 6674462Salfred.if ${0 \# comment:?yes:no} != no 6774462Salfred. error 6874462Salfred.endif 6974462Salfred.if ${1 \# comment:?yes:no} != yes 7074462Salfred. error 7174462Salfred.endif 7274462Salfred 7374462Salfred# Usually there is whitespace around the comparison operator, but this is 7474462Salfred# not required. 7574462Salfred.if ${UNDEF:Uundefined}!=undefined 7674462Salfred. error 7774462Salfred.endif 7874462Salfred.if ${UNDEF:U12345}>12345 7974462Salfred. error 8074462Salfred.endif 8174462Salfred.if ${UNDEF:U12345}<12345 8274462Salfred. error 8374462Salfred.endif 8474462Salfred.if (${UNDEF:U0})||0 8574462Salfred. error 8674462Salfred.endif 8774462Salfred 8874462Salfred# Only the comparison operator terminates the comparison operand, and it's 8974462Salfred# a coincidence that the '!' is both used in the '!=' comparison operator 9074462Salfred# as well as for negating a comparison result. 9174462Salfred# 9274462Salfred# The characters '&' and '|' are part of the comparison operand. 9374462Salfred.if ${:Uvar}&&name != "var&&name" 9474462Salfred. error 9574462Salfred.endif 9674462Salfred.if ${:Uvar}||name != "var||name" 9774462Salfred. error 9874462Salfred.endif 9974462Salfred 10074462Salfred# A bare word may occur alone in a condition, without any comparison 10174462Salfred# operator. It is interpreted as the function call 'defined(bare)'. 10274462Salfred.if bare 10374462Salfred. error 10474462Salfred.else 10574462Salfred# expect+1: A bare word is treated like defined(...), and the variable 'bare' is not defined. 10674462Salfred. info A bare word is treated like defined(...), and the variable $\ 10774462Salfred 'bare' is not defined. 10874462Salfred.endif 10974462Salfred 11074462SalfredVAR= defined 11174462Salfred.if VAR 11274462Salfred# expect+1: A bare word is treated like defined(...). 11374462Salfred. info A bare word is treated like defined(...). 11474462Salfred.else 11574462Salfred. error 11674462Salfred.endif 11774462Salfred 11874462Salfred# Bare words may be intermixed with expressions. 11974462Salfred.if V${:UA}R 12074462Salfred# expect+1: ok 12174462Salfred. info ok 12274462Salfred.else 12374462Salfred. error 12474462Salfred.endif 12574462Salfred 12674462Salfred# In bare words, even undefined variables are allowed. Without the bare 12774462Salfred# words, undefined variables are not allowed. That feels inconsistent. 12874462Salfred.if V${UNDEF}AR 12974462Salfred# expect+1: Undefined variables in bare words expand to an empty string. 13074462Salfred. info Undefined variables in bare words expand to an empty string. 13174462Salfred.else 13274462Salfred. error 13374462Salfred.endif 13474462Salfred 13574462Salfred.if 0${:Ux00} 13674462Salfred. error 13774462Salfred.else 13874462Salfred# expect+1: Numbers can be composed from literals and expressions. 13974462Salfred. info Numbers can be composed from literals and expressions. 14074462Salfred.endif 14174462Salfred 14274462Salfred.if 0${:Ux01} 14374462Salfred# expect+1: Numbers can be composed from literals and expressions. 14474462Salfred. info Numbers can be composed from literals and expressions. 14574462Salfred.else 14674462Salfred. error 14774462Salfred.endif 14874462Salfred 14974462Salfred# If the right-hand side is missing, it's a parse error. 15074462Salfred# expect+1: Missing right-hand side of operator '==' 15174462Salfred.if "" == 15274462Salfred. error 15374462Salfred.else 15474462Salfred. error 15574462Salfred.endif 15674462Salfred 15774462Salfred# If the left-hand side is missing, it's a parse error as well, but without 15874462Salfred# a specific error message. 15974462Salfred# expect+1: Malformed conditional (== "") 16074462Salfred.if == "" 16174462Salfred. error 16274462Salfred.else 16374462Salfred. error 16474462Salfred.endif 16574462Salfred 16674462Salfred# The '\\' is not a line continuation. Neither is it an unquoted string 16774462Salfred# literal. Instead, it is parsed as a bare word (ParseWord), 16874462Salfred# and in that context, the backslash is just an ordinary character. The 16974462Salfred# function argument thus stays '\\' (2 backslashes). This string is passed 17074462Salfred# to FuncDefined, and since there is no variable named '\\', the condition 17174462Salfred# evaluates to false. 17274462Salfred.if \\ 17374462Salfred. error 17474462Salfred.else 17574462Salfred# expect+1: The variable '\\' is not defined. 17674462Salfred. info The variable '\\' is not defined. 17774462Salfred.endif 17874462Salfred 17974462Salfred${:U\\\\}= backslash 18074462Salfred.if \\ 18174462Salfred# expect+1: Now the variable '\\' is defined. 18274462Salfred. info Now the variable '\\' is defined. 18374462Salfred.else 18474462Salfred. error 18574462Salfred.endif 18674462Salfred 18774462Salfred# Anything that doesn't start with a double quote is considered a "bare word". 18874462Salfred# Strangely, a bare word may contain double quotes inside. Nobody should ever 18974462Salfred# depend on this since it may well be unintended. See CondParser_String. 19074462Salfred.if "unquoted\"quoted" != unquoted"quoted 19174462Salfred. error 19274462Salfred.endif 19374462Salfred 19474462Salfred# FIXME: In CondParser_String, Var_Parse returns var_Error without a 19574462Salfred# corresponding error message. 19674462Salfred# expect+1: Malformed conditional ($$$$$$$$ != "") 19774462Salfred.if $$$$$$$$ != "" 19874462Salfred. error 19974462Salfred.else 20074462Salfred. error 20174462Salfred.endif 20274462Salfred 20374462Salfred# In a condition in an .if directive, the left-hand side must not be an 20474462Salfred# unquoted string literal. 20574462Salfred# expect+1: Malformed conditional (left == right) 20674462Salfred.if left == right 20774462Salfred.endif 20874462Salfred# Before cond.c 1.276 from 2021-09-21, an expression containing the 20974462Salfred# modifier ':?:' allowed unquoted string literals for the rest of the 21074462Salfred# condition. This was an unintended implementation mistake. 21174462Salfred# expect+1: Malformed conditional (${0:?:} || left == right) 21274462Salfred.if ${0:?:} || left == right 21374462Salfred.endif 21474462Salfred# This affected only the comparisons after the expression, so the following 21574462Salfred# was still a syntax error. 21674462Salfred# expect+1: Malformed conditional (left == right || ${0:?:}) 21774462Salfred.if left == right || ${0:?:} 21874462Salfred.endif 21974462Salfred 22074462Salfred# See cond-token-string.mk for similar tests where the condition is enclosed 22174462Salfred# in "quotes". 22274462Salfred 22374462Salfred.MAKEFLAGS: -d0 22474462Salfred 22574462Salfred 22674462Salfred# As of cond.c 1.320 from 2021-12-30, the code in CondParser_ComparisonOrLeaf 22774462Salfred# looks suspicious of evaluating the expression twice: first for parsing a 22874462Salfred# bare word and second for parsing the left-hand side of a comparison. 22974462Salfred# 23074462Salfred# In '.if' directives, the left-hand side of a comparison must not be a bare 23174462Salfred# word though, and this keeps CondParser_Leaf from evaluating the expression 23274462Salfred# for the second time. The right-hand side of a comparison may be a bare 23374462Salfred# word, but that side has no risk of being parsed more than once. 23474462Salfred# 23592223Sobrien# expect+1: Malformed conditional (VAR.${IF_COUNT::+=1} != "") 23674462Salfred.if VAR.${IF_COUNT::+=1} != "" 23774462Salfred. error 23874462Salfred.else 23974462Salfred. error 24074462Salfred.endif 24174462Salfred.if ${IF_COUNT} != "1" 24274462Salfred. error 24374462Salfred.endif 24474462Salfred 24574462Salfred# A different situation is when CondParser.leftUnquotedOK is true. This 24674462Salfred# situation arises in expressions of the form ${cond:?yes:no}. As of 24774462Salfred# 2021-12-30, the condition in such an expression is evaluated before parsing 24874462Salfred# the condition, see varmod-ifelse.mk. To pass an expression to the 24974462Salfred# condition parser, it needs to be escaped. This rarely happens in practice, 25074462Salfred# in most cases the conditions are simple enough that it doesn't matter 25174462Salfred# whether the condition is first evaluated and then parsed, or vice versa. 25274462Salfred# A half-baked attempt at hiding this implementation detail is 25374462Salfred# CondParser.leftUnquotedOK, but that is a rather leaky abstraction. 25474462Salfred 25574462Salfred#.MAKEFLAGS: -dcv 25674462SalfredCOND= VAR.$${MOD_COUNT::+=1} 25774462Salfred.if ${${COND} == "VAR.":?yes:no} != "yes" 25874462Salfred. error 25974462Salfred.endif 26074462Salfred 26174462Salfred# The value "1 1" demonstrates that the expression ${MOD_COUNT::+=1} was 26274462Salfred# evaluated twice. In practice, expressions that occur in conditions do not 26374462Salfred# have side effects, making this problem rather academic, but it is there. 26474462Salfred.if ${MOD_COUNT} != "1 1" 26574462Salfred. error 26674462Salfred.endif 26774462Salfred#.MAKEFLAGS: -d0 26874462Salfred