Re: passing a parenthesis character

Eric Blake <[email protected]> Thu, 8 Mar 2018 15:58:41 -0600
Newsgroups gmane.comp.gnu.m4.general
Organization Red Hat, Inc.
Message-ID <[email protected]>
On 03/08/2018 03:29 PM, Denis Valois wrote:
> Hello m4 wizards,
> 
> My problem is passing a single parenthesis as a macro argument.  I do
> not understand m4 behavior; this tells me that I miss some fundamentals.
> 
> example 1:  notice the litteral `('
>      index(`abcdef', `(')
> this works perfectly with the expected results -1.
> 
> example 2: use of a macro
>      define(`c', `(')
>      index(`abcdef', c)
> Here we have "ERROR: end of file in argument list".  This is because the
> macro c is expanded before index so the parenthesis are unbalanced.

Yep. During argument collection, m4 groups all text between unquoted () 
as part of a single argument, so you have to have balanced () if they 
are unquoted.  And once the macro "c" is expanded, you have introduced 
an unquoted ( into the stream that m4 is processing:

index(`abcdef', ()

as that lacks a closing ), you get the syntax error about end of file.

(Why are unquoted parenthesis special? So you can do things like:

define(`cat', `$1$2')
cat(`index', (`abcdef', `b'))

which passes only two (not three) arguments to cat(), and is a long-hand 
way of writing index(`abcdef', `b').  Annoyingly, unpaired parenthesis 
are more common than you think: in autoconf, shell case statements tend 
to have more ')' than '(', leading to tricks like:

case $foo in # for balance (
   bar) ... ;;
esac

in the input handed to m4.)

> 
> Other approaches such as
>      builtin(`index', `abcdef', `c')

That expanded the same as:

index(`abcdef', `c')

which is not the contents of macro c like you wanted.

> are either expanding to a wrong result or generate a run-time error.  I
> simply do not see how to pass a macro defined with a parenthesis as
> argument.

It sounds like you want:

index(`abcdef', defn(`c'))

the defn() macro outputs the quoted value of macro c, which is then 
equivalent to what you originally used with a literal quoted string.

Another thing you could try is defining "c" to be a quoted rather than 
bare unmatched parenthesis:

define(`c', ``('')

which may be what you want depending on where you expand the macro "c".

In general, life with mismatched parenthesis can be tricky, but it's 
puzzlers like that which are fun to help on, so feel free to ask further 
related questions.

-- 
Eric Blake, Principal Software Engineer
Red Hat, Inc.           +1-919-301-3266
Virtualization:  qemu.org | libvirt.org