m4: Changequote

1 
1 8.2 Changing the quote characters
1 =================================
1 
1 The default quote delimiters can be changed with the builtin
1 'changequote':
1 
1  -- Builtin: changequote ([START = '`'], [END = '''])
1      This sets START as the new begin-quote delimiter and END as the new
1      end-quote delimiter.  If both arguments are missing, the default
1      quotes ('`' and ''') are used.  If START is void, then quoting is
1      disabled.  Otherwise, if END is missing or void, the default
1      end-quote delimiter (''') is used.  The quote delimiters can be of
1      any length.
1 
1      The expansion of 'changequote' is void.
1 
1      changequote(`[', `]')
1      =>
1      define([foo], [Macro [foo].])
1      =>
1      foo
1      =>Macro foo.
1 
1    The quotation strings can safely contain eight-bit characters.  If no
1 single character is appropriate, START and END can be of any length.
1 Other implementations cap the delimiter length to five characters, but
1 GNU has no inherent limit.
1 
1      changequote(`[[[', `]]]')
1      =>
1      define([[[foo]]], [[[Macro [[[[[foo]]]]].]]])
1      =>
1      foo
1      =>Macro [[foo]].
1 
1    Calling 'changequote' with START as the empty string will effectively
1 disable the quoting mechanism, leaving no way to quote text.  However,
1 using an empty string is not portable, as some other implementations of
1 'm4' revert to the default quoting, while others preserve the prior
1 non-empty delimiter.  If START is not empty, then an empty END will use
1 the default end-quote delimiter of ''', as otherwise, it would be
1 impossible to end a quoted string.  Again, this is not portable, as some
1 other 'm4' implementations reuse START as the end-quote delimiter, while
1 others preserve the previous non-empty value.  Omitting both arguments
1 restores the default begin-quote and end-quote delimiters; fortunately
1 this behavior is portable to all implementations of 'm4'.
1 
1      define(`foo', `Macro `FOO'.')
1      =>
1      changequote(`', `')
1      =>
1      foo
1      =>Macro `FOO'.
1      `foo'
1      =>`Macro `FOO'.'
1      changequote(`,)
1      =>
1      foo
1      =>Macro FOO.
1 
1    There is no way in 'm4' to quote a string containing an unmatched
1 begin-quote, except using 'changequote' to change the current quotes.
1 
1    If the quotes should be changed from, say, '[' to '[[', temporary
1 quote characters have to be defined.  To achieve this, two calls of
1 'changequote' must be made, one for the temporary quotes and one for the
1 new quotes.
1 
1    Macros are recognized in preference to the begin-quote string, so if
1 a prefix of START can be recognized as part of a potential macro name,
1 the quoting mechanism is effectively disabled.  Unless you use
1 'changeword' (⇒Changeword), this means that START should not
1 begin with a letter, digit, or '_' (underscore).  However, even though
1 quoted strings are not recognized, the quote characters can still be
1 discerned in macro expansion and in trace output.
1 
1      define(`echo', `$@')
1      =>
1      define(`hi', `HI')
1      =>
1      changequote(`q', `Q')
1      =>
1      q hi Q hi
1      =>q HI Q HI
1      echo(hi)
1      =>qHIQ
1      changequote
1      =>
1      changequote(`-', `EOF')
1      =>
1      - hi EOF hi
1      => hi  HI
1      changequote
1      =>
1      changequote(`1', `2')
1      =>
1      hi1hi2
1      =>hi1hi2
1      hi 1hi2
1      =>HI hi
1 
1    Quotes are recognized in preference to argument collection.  In
1 particular, if START is a single '(', then argument collection is
1 effectively disabled.  For portability with other implementations, it is
1 a good idea to avoid '(', ',', and ')' as the first character in START.
1 
1      define(`echo', `$#:$@:')
1      =>
1      define(`hi', `HI')
1      =>
1      changequote(`(',`)')
1      =>
1      echo(hi)
1      =>0::hi
1      changequote
1      =>
1      changequote(`((', `))')
1      =>
1      echo(hi)
1      =>1:HI:
1      echo((hi))
1      =>0::hi
1      changequote
1      =>
1      changequote(`,', `)')
1      =>
1      echo(hi,hi)bye)
1      =>1:HIhibye:
1 
1    However, if you are not worried about portability, using '(' and ')'
1 as quoting characters has an interesting property--you can use it to
1 compute a quoted string containing the expansion of any quoted text, as
1 long as the expansion results in both balanced quotes and balanced
1 parentheses.  The trick is realizing 'expand' uses '$1' unquoted, to
1 trigger its expansion using the normal quoting characters, but uses
1 extra parentheses to group unquoted commas that occur in the expansion
1 without consuming whitespace following those commas.  Then '_expand'
1 uses 'changequote' to convert the extra parentheses back into quoting
1 characters.  Note that it takes two more 'changequote' invocations to
1 restore the original quotes.  Contrast the behavior on whitespace when
1 using '$*', via 'quote', to attempt the same task.
1 
1      changequote(`[', `]')dnl
1      define([a], [1, (b)])dnl
1      define([b], [2])dnl
1      define([quote], [[$*]])dnl
1      define([expand], [_$0(($1))])dnl
1      define([_expand],
1        [changequote([(], [)])$1changequote`'changequote(`[', `]')])dnl
1      expand([a, a, [a, a], [[a, a]]])
1      =>1, (2), 1, (2), a, a, [a, a]
1      quote(a, a, [a, a], [[a, a]])
1      =>1,(2),1,(2),a, a,[a, a]
1 
1    If END is a prefix of START, the end-quote will be recognized in
1 preference to a nested begin-quote.  In particular, changing the quotes
1 to have the same string for START and END disables nesting of quotes.
1 When quote nesting is disabled, it is impossible to double-quote strings
1 across macro expansions, so using the same string is not done very
1 often.
1 
1      define(`hi', `HI')
1      =>
1      changequote(`""', `"')
1      =>
1      ""hi"""hi"
1      =>hihi
1      ""hi" ""hi"
1      =>hi hi
1      ""hi"" "hi"
1      =>hi" "HI"
1      changequote
1      =>
1      `hi`hi'hi'
1      =>hi`hi'hi
1      changequote(`"', `"')
1      =>
1      "hi"hi"hi"
1      =>hiHIhi
1 
1    It is an error if the end of file occurs within a quoted string.
1 
1      `hello world'
1      =>hello world
1      `dangling quote
1      ^D
1      error->m4:stdin:2: ERROR: end of file in string
1 
1      ifelse(`dangling quote
1      ^D
1      error->m4:stdin:1: ERROR: end of file in string
1