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