[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: nonuniform behavior of builtins
From: |
Eric Blake |
Subject: |
Re: nonuniform behavior of builtins |
Date: |
Wed, 11 Sep 2024 10:53:41 -0500 |
User-agent: |
NeoMutt/20240425 |
On Tue, Sep 10, 2024 at 05:36:21PM GMT, Douglas McIlroy wrote:
> It is annoying that various builtin macros don't argument lists like
> ordinary macros do.
You are right that several builtins behave differently for zero
arguments than they do for arguments provided (even if there is just 1
empty argument). But you can write ordinary macros that treat 0
arguments different than 1 empty argument, and so forth (by using the
ifelse macro and $#); so it is not unique to builtins.
Below, I'll refer to the POSIX specification for m4:
https://pubs.opengroup.org/onlinepubs/9799919799/utilities/m4.html
>
> dnl complains if given an argument list--even an empty one. info m4 has a
> long discussion about how dnl treats arguments, even though the treatment
> is exactly the same as for non-builtin macros. When the argument list is
> empty, the diagnostic wrongly says there are too many arguments. When
> arguments are present, they are collected and never used, just as happens
> for extra arguments for any macro call. Why should dnl care, when ordinary
> macros don't? The diagnostic is just plain silly.
Maybe, but consider the following, and what you think should be the
correct behavior for:
dnl ` would be an unterminated string, but discarding took precedence
this is macro-expanded
dnl define(`foo', `discarding also trumps unterminated macro definition
dnl( this is ignored
) but your are asking if this should be ignored
POSIX says dnl discards input characters up to the next newline, but
does not mention whether that is before or after arguments; and
stylistically, m4 code has never been written with dnl() comments (if
anything, GNU's warning has served to encourage a particular coding
style). Removing the diagnostic would make it easier to write
non-portable m4.
>
> undefine is not recognized (i.e. it is copied to the output) when it lacks
> an argument list. undefine(), however, is recognized and does nothing--the
> expected limiting behavior. Ordinary macros treat a missing argument list
> as an empty list. undefine's special behavior adds complexity to m4 and its
> documentation for no apparent reason.
Actually, in GNU m4, undefine IS recognized when called without an
argument list, but specially defined so that the output with 0
arguments is the quoted string `undefine' (aka act as if the macro was
not invoked). Furthermore, undefine() is NOT a no-op: GNU m4 lets you
define the empty string as a macro name. You can't invoke it
directly, but you can get its definition:
define(, empty text)dnl
defn()
empty text
undefine()dnl
defn()-
-
POSIX says the behavior is unspecified when undefine is invoked without
arguments, so portable m4 should be writing `undefine' when it wants
the output to use the word literally; but since POSIX does not mandate
behavior, using -G doesn't need to change GNU's choice of behavior.
POSIX also says it is nonportable to define a macro that does not
consist of a name (aka something that can be recognized as a macro
name later on), which excludes the portable use of the empty string as
a macro name. It is a GNU extension that you can define arbitrary
strings, including non-macro-names, to reuse the definition space for
arbitrary key-based storage lookup, but there is no reason to disable
this extension for -G (that is, -G does NOT mean the same as
"forcefully reject all attempts to use a non-POSIX feature", but
rather means "if the GNU default behavior disagrees with POSIX, use
the POSIX behavior").
>
> Option -G does not affect either of these behaviors. I find it hard to
> believe that either Kernighan or Ritchie would perpetrate such
> nonuniformity. Does anybody know whether it has any historical
> justification?
GNU m4 has been doing it that way for more than 30 years. Changing it
now may be a gratuitous change to software that has come to expect GNU
behavior.
>
> Several other builtins share undefine's anomalous behavior for a missing
> argument list. Again, special behavior entails extra documentation for each
> such builtin.
Yes, but such documentation exists, so you can't complain that it is
unexpected, even if it requires some verbosity and disagrees with your
own personal expectations were m4 being designed from scratch today.
>
> I think the dnl diagnostic should be canned. I also lament the nonuniform
> treatment of missing argument lists, and suggest it be phased out: when
> undefine and its ilk are copied to output warn that the bevior is scheduled
> to change.
That's easy enough to do for your own scripts:
define(`realdnl', defn(`dnl'))dnl
define(`dnl', `realdnl`'')dnl() see what I just did?
define(`realundefine', defn(`undefine'))dnl
define(`undefine', `ifelse(`$#', 0, `errprint(`m4:'__file__:__line__`: Warning:
missing argument to `$0'
')', `realundefine($@)')')
--
Eric Blake, Principal Software Engineer
Red Hat, Inc.
Virtualization: qemu.org | libguestfs.org