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