# -*- Autotest -*- AT_BANNER([M4sugar.]) # Copyright (C) 2000-2002, 2005-2017, 2020-2023 Free Software # Foundation, Inc. # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # AT_CHECK_M4SUGAR_TEXT(CODE, STDOUT, STDERR) # ------------------------------------------- # Check that m4sugar CODE expands to STDOUT and emits STDERR. m4_define([AT_CHECK_M4SUGAR_TEXT], [ AT_DATA_M4SUGAR([script.4s], [[m4_init m4_divert_push([])[]dnl ]$1[[]dnl m4_divert_pop([]) ]]) AT_CHECK_M4SUGAR([-o-],, [$2], [$3]) ])# AT_CHECK_M4SUGAR_TEXT ## ------------------ ## ## m4_stack_foreach. ## ## ------------------ ## AT_SETUP([m4@&t@_stack]) AT_KEYWORDS([m4@&t@_stack_foreach m4@&t@_stack_foreach_lifo]) AT_KEYWORDS([m4@&t@_stack_foreach_sep m4@&t@_stack_foreach_sep_lifo]) AT_KEYWORDS([m4@&t@_copy m4@&t@_n]) # Test the semantics of macros to walk stacked macro definitions. AT_CHECK_M4SUGAR_TEXT([[dnl m4_pushdef([abc], [def])dnl m4_pushdef([abc], [ghi])dnl m4_pushdef([abc], [jkl])dnl m4_stack_foreach([abc], [m4_n]) abc m4_stack_foreach_lifo([abc], [m4_n]) m4_stack_foreach([abc], [m4_n]) m4_copy([abc], [foo])dnl m4_stack_foreach([foo], [m4_n]) m4_stack_foreach_lifo([foo], [m4_n]) m4_stack_foreach_sep([abc], [ m4_index([abcdefghijkl],], [)]) m4_define([colon], [:])m4_define([lt], [<])m4_define([gt], [>])dnl m4_stack_foreach_sep_lifo([abc], [lt], [gt], [colon]) m4_pushdef([xyz], [123])dnl m4_pushdef([xyz], [456])dnl m4_define([doit], [[$1](m4_stack_foreach_sep([xyz], [m4_dquote(], [)], [,])) ])dnl m4_stack_foreach([abc], [doit])]], [[def ghi jkl jkl jkl ghi def def ghi jkl def ghi jkl jkl ghi def 3 6 9 :: def([123],[456]) ghi([123],[456]) jkl([123],[456]) ]]) AT_CLEANUP ## --------- ## ## m4_defn. ## ## --------- ## AT_SETUP([m4@&t@_defn]) AT_KEYWORDS([m4@&t@_popdef m4@&t@_undefine m4@&t@_copy m4@&t@_rename m4@&t@_copy_force m4@&t@_rename_force]) # Ensure that m4sugar dies when dereferencing undefined macros, whether # this is provided by m4 natively or faked by wrappers in m4sugar. AT_DATA_M4SUGAR([script.4s], [[m4_define([good]) m4_defn([good], [oops]) ]]) AT_CHECK_M4SUGAR([-o-], 1, [], [stderr]) AT_CHECK([grep good stderr], [1]) AT_CHECK([grep 'm4@&t@_defn: undefined.*oops' stderr], [0], [ignore]) AT_DATA_M4SUGAR([script.4s], [[m4_define([good]) m4_popdef([good], [oops]) ]]) AT_CHECK_M4SUGAR([-o-], 1, [], [stderr]) AT_CHECK([grep good stderr], [1]) AT_CHECK([grep 'm4@&t@_popdef: undefined.*oops' stderr], [0], [ignore]) AT_DATA_M4SUGAR([script.4s], [[m4_define([good]) m4_undefine([good], [oops]) ]]) AT_CHECK_M4SUGAR([-o-], 1, [], [stderr]) AT_CHECK([grep good stderr], [1]) AT_CHECK([grep 'm4@&t@_undefine: undefined.*oops' stderr], [0], [ignore]) # Cannot rename an undefined macro. AT_DATA_M4SUGAR([script.4s], [[m4_rename([oops], [good]) ]]) AT_CHECK_M4SUGAR([-o-], 1, [], [stderr]) AT_CHECK([grep 'm4@&t@_undefine: undefined.*oops' stderr], [0], [ignore]) # Check that pushdef stacks can be renamed. AT_CHECK_M4SUGAR_TEXT([[m4_pushdef([a], [1])dnl m4_pushdef([a], [2])dnl m4_pushdef([a], m4_defn([m4_divnum]))dnl a b c m4_rename([a], [b])dnl a b c m4_copy([b], [c])dnl a b c m4_popdef([b], [c])dnl a b c m4_popdef([b], [c])dnl a b c m4_popdef([b], [c])dnl a b c dnl m4_copy is intentionally a no-op on undefined source m4_copy([oops], [dummy])m4_ifdef([dummy], [[oops]])dnl dnl allow forceful overwrites m4_define([d], [4])m4_define([e], [5])m4_define([f], [6])dnl m4_copy_force([d], [e])dnl m4_rename_force([d], [f])dnl d e f m4_popdef([e], [f])dnl d e f ]], [[0 b c a 0 c a 0 0 a 2 2 a 1 1 a b c d 4 4 d e f ]]) AT_CLEANUP ## ------------ ## ## m4_dumpdef. ## ## ------------ ## AT_SETUP([m4@&t@_dumpdef]) AT_KEYWORDS([m4@&t@_dumpdefs]) # Ensure that m4sugar dies when dereferencing undefined macros. AT_DATA_M4SUGAR([script.4s], [[m4_define([good], [yep]) m4_dumpdef([good], [oops]) ]]) AT_CHECK_M4SUGAR([-o-], 1, [], [stderr]) AT_CHECK([grep '^good: \[[yep]]$' stderr], [0], [ignore]) AT_CHECK([grep 'm4@&t@_dumpdef: undefined.*oops' stderr], [0], [ignore]) # Check that pushdef stacks can be dumped. AT_CHECK_M4SUGAR_TEXT([[m4_divert_push([KILL]) m4_pushdef([a], [1]) m4_pushdef([a], [2]) m4_dumpdef([a]) m4_dumpdefs([oops], [a]) m4_divert_pop([KILL])dnl ]], [], [[a: [2] a: [2] a: [1] ]]) # Check behavior when dumping builtins. Unfortunately, when using M4 1.4.x # (or more precisely, when __m4_version__ is undefined), builtins get # flattened to an empty string. It takes M4 1.6 to work around this. AT_DATA_M4SUGAR([script.4s], [[m4_ifdef([__m4_version__], [_m4_undefine([__m4_version__])]) m4_init m4_dumpdef([m4_define]) ]]) AT_CHECK_M4SUGAR([-o-], [0], [], [[m4_define: [] ]]) AT_DATA_M4SUGAR([script.4s], [[m4_init m4_ifdef([__m4_version__], [m4_dumpdef([m4_define])], [m4_errprintn([m4_define: ])]) ]]) AT_CHECK_M4SUGAR([-o-], [0], [], [[m4_define: ]]) AT_CLEANUP ## --------- ## ## m4_warn. ## ## --------- ## AT_SETUP([m4@&t@_warn]) AT_DATA_M4SUGAR([script.4s], [[m4_init m4_defun([cross_warning], [m4_warn([cross], [cross])]) m4_divert([0])dnl m4_warn([obsolete], [obsolete])dnl cross_warning[]dnl m4_warn([syntax], [syntax])dnl cross_warning[]dnl m4_warn([syntax], [syntax])dnl ]]) AT_CHECK_M4SUGAR([-o-], 0, [], [script.4s:4: warning: prefer named diversions script.4s:5: warning: obsolete script.4s:7: warning: syntax script.4s:9: warning: syntax ]) AT_CHECK_M4SUGAR([-o- -Wall], 0, [], [script.4s:4: warning: prefer named diversions script.4s:5: warning: obsolete script.4s:6: warning: cross script.4s:2: cross_warning is expanded from... script.4s:6: the top level script.4s:7: warning: syntax script.4s:8: warning: cross script.4s:2: cross_warning is expanded from... script.4s:8: the top level script.4s:9: warning: syntax ]) AT_CHECK_M4SUGAR([-o- -Wnone,cross], 0, [], [script.4s:6: warning: cross script.4s:2: cross_warning is expanded from... script.4s:6: the top level script.4s:8: warning: cross script.4s:2: cross_warning is expanded from... script.4s:8: the top level ]) AT_CHECK_M4SUGAR([-o- -Wnone,cross,error], 1, [], [[script.4s:6: warning: cross script.4s:2: cross_warning is expanded from... script.4s:6: the top level script.4s:8: warning: cross script.4s:2: cross_warning is expanded from... script.4s:8: the top level ]]) AT_CLEANUP ## ----------------- ## ## m4_divert_stack. ## ## ----------------- ## AT_SETUP([m4@&t@_divert_stack]) AT_KEYWORDS([m4@&t@_divert m4@&t@_divert_push m4@&t@_divert_pop m4@&t@_undivert m4@&t@_cleardivert m4@&t@_divert_text]) dnl This test names some diversions to avoid a warning. AT_CHECK_M4SUGAR_TEXT([[m4_define([_m4_divert(ten)], [10])dnl m4_define([_m4_divert(twenty)], [20])dnl m4_define([_m4_divert(thirty)], [30])dnl 1.m4_divert_stack m4_divert_push([ten])2.m4_divert_stack m4_divert_text([twenty], [3.m4_divert_stack])dnl m4_divert([thirty])4.m4_divert_stack m4_divert_pop([thirty])dnl 5.m4_undivert([twenty], [thirty]) m4_pattern_allow([^m4_divert])dnl ]], [[1.script.4s:2: m4@&t@_divert_push: script.4s:1: m4@&t@_divert: KILL 5.3.script.4s:8: m4@&t@_divert_push: twenty script.4s:7: m4@&t@_divert_push: ten script.4s:2: m4@&t@_divert_push: script.4s:1: m4@&t@_divert: KILL 4.script.4s:9: m4@&t@_divert: thirty script.4s:2: m4@&t@_divert_push: script.4s:1: m4@&t@_divert: KILL 2.script.4s:7: m4@&t@_divert_push: ten script.4s:2: m4@&t@_divert_push: script.4s:1: m4@&t@_divert: KILL ]]) AT_CHECK_M4SUGAR_TEXT([[dnl m4_divert_text([3], [three])dnl m4_divert_text([4], [four])dnl m4_divert_text([1], [one])dnl m4_divert_text([2], [two])dnl m4_cleardivert([2], [3])dnl ]], [[one four ]], [[script.4s:4: warning: prefer named diversions script.4s:5: warning: prefer named diversions script.4s:6: warning: prefer named diversions script.4s:7: warning: prefer named diversions script.4s:8: warning: prefer named diversions ]]) AT_DATA_M4SUGAR([script.4s], [[m4_divert_pop ]]) AT_CHECK_M4SUGAR([-o-], [1], [], [[script.4s:1: error: too many m4@&t@_divert_pop script.4s:1: the top level autom4te: error: m4 failed with exit status: 1 ]]) AT_DATA_M4SUGAR([script.4s], [[m4_init m4_divert_push([1]) m4_divert_pop([2]) ]]) AT_CHECK_M4SUGAR([-o-], [1], [], [[script.4s:3: error: m4@&t@_divert_pop(2): diversion mismatch: script.4s:2: m4@&t@_divert_push: 1 script.4s:1: m4@&t@_divert: KILL script.4s:3: the top level autom4te: error: m4 failed with exit status: 1 ]]) AT_DATA_M4SUGAR([script.4s], [[m4_divert([1]) m4_init m4_divert_push([2]) ]]) AT_CHECK_M4SUGAR([-o-], [1], [], [[script.4s:2: error: m4@&t@_init: unbalanced m4@&t@_divert_push: script.4s:3: m4@&t@_divert_push: 2 script.4s:2: m4@&t@_divert: KILL script.4s:2: the top level autom4te: error: m4 failed with exit status: 1 ]]) AT_CLEANUP ## -------------------- ## ## m4_expansion_stack. ## ## -------------------- ## AT_SETUP([m4@&t@_expansion_stack]) AT_CHECK_M4SUGAR_TEXT([[1.m4_expansion_stack m4_defun([a], [b])dnl m4_define([c], [d])dnl m4_defun([d], [2.m4_expansion_stack])dnl m4_defun([b], [c])dnl a 3.m4_ifdef([_m4_expansion_stack], [m4_expansion_stack]) ]], [[1.script.4s:3: the top level 2.script.4s:6: d is expanded from... script.4s:7: b is expanded from... script.4s:4: a is expanded from... script.4s:8: the top level 3. ]]) AT_CLEANUP ## --------------------------- ## ## m4_require: error message. ## ## --------------------------- ## AT_SETUP([m4@&t@_require: error message]) AT_KEYWORDS([m4@&t@_require]) AT_DATA_M4SUGAR([script.4s], [[m4_defun([foo], [FOO]) m4_require([foo]) ]]) AT_CHECK_M4SUGAR([], 1, [], [[script.4s:2: error: m4@&t@_require(foo): cannot be used outside of an m4_defun'd macro script.4s:2: the top level autom4te: error: m4 failed with exit status: 1 ]]) AT_CLEANUP ## ----------------------------- ## ## m4_require: warning message. ## ## ----------------------------- ## AT_SETUP([m4@&t@_require: warning message]) AT_KEYWORDS([m4@&t@_require m4@&t@_require_silent_probe]) # Mirror the job of aclocal on a typical scenario: the user invokes a # single macro that comes from one included file, which in turn requires # another macro from a second file. When using the incomplete set of # files, we want a warning, unless we are merely learning which additional # macros are needed in order to regenerate the list of files to include. AT_DATA_M4SUGAR([script.4s], [[m4_init m4_include([script1.4s]) foo ]]) AT_DATA_M4SUGAR([script1.4s], [[m4_defun([foo], [m4_require([bar])]) ]]) AT_DATA_M4SUGAR([script2.4s], [[m4_defun([bar], [BAR]) ]]) AT_CHECK_M4SUGAR([], [0], [], [[script.4s:3: warning: bar is m4@&t@_require'd but not m4@&t@_defun'd script1.4s:1: foo is expanded from... script.4s:3: the top level ]]) # Inline expansion of AT_CHECK_M4SUGAR, mirroring how aclocal will # inject a definition of our witness macro for a silent run. echo 'm4@&t@_define([m4@&t@_require_silent_probe])' | AT_CHECK_AUTOM4TE([--language=m4sugar - script.4s -o script], [0], [], []) # Now that we have recomputed the set of include files, things should work. AT_DATA_M4SUGAR([script.4s], [[m4_init m4_include([script1.4s]) m4_include([script2.4s]) foo ]]) AT_CHECK_M4SUGAR([], [0], [], []) AT_CLEANUP ## ----------------------------------- ## ## m4_require: circular dependencies. ## ## ----------------------------------- ## AT_SETUP([m4@&t@_require: circular dependencies]) AT_KEYWORDS([m4@&t@_require]) AT_DATA_M4SUGAR([script.4s], [[m4_defun([foo], [m4_require([bar])]) m4_defun([bar], [m4_require([foo])]) m4_defun([baz], [m4_require([foo])]) m4_init m4_divert([0])dnl baz ]]) AT_CHECK_M4SUGAR([], 1, [], [[script.4s:9: error: m4@&t@_require: circular dependency of foo script.4s:3: bar is expanded from... script.4s:1: foo is expanded from... script.4s:5: baz is expanded from... script.4s:9: the top level autom4te: error: m4 failed with exit status: 1 ]]) AT_CLEANUP ## ---------------------- ## ## m4_require: one-shot. ## ## ---------------------- ## AT_SETUP([m4@&t@_require: one-shot initialization]) AT_KEYWORDS([m4@&t@_require]) AT_KEYWORDS([m4@&t@_defun_init m4@&t@_copy m4@&t@_defun_once]) dnl check out m4_defun_init, m4_copy, and odd macro names AT_CHECK_M4SUGAR_TEXT([[ m4_define([t], [text])dnl m4_defun_init([a], [[init a ]], [[common a] t])dnl m4_defun([b], [[b]m4_require([a])])dnl m4_defun([c], [[c]m4_require([a])])dnl b c a()dnl m4_defun_init([-], [hello, ], [m4_if([$#], [0], [world], [[$1]])])dnl m4_copy([-], [.])dnl m4_indir([.]) m4_indir([.], [goodbye]) m4_indir([-], [again]) ]], [[ init a common a text b c common a text hello, world goodbye hello, again ]]) dnl Check m4_defun_once behavior AT_CHECK_M4SUGAR_TEXT([[ m4_defun_once([a], [[a]])dnl m4_defun([b], [[b]m4_require([a])])dnl m4_defun([c], [[c]a[]m4_require([b])])dnl c a m4_defun_once([d], [[d]m4_require([a])])dnl d m4_defun_once([e], [[e]])dnl m4_defun([f], [[f]m4_require([e])e])dnl f ]], [[ a b c d e f ]]) AT_CLEANUP ## -------------------- ## ## m4_require: nested. ## ## -------------------- ## AT_SETUP([m4@&t@_require: nested]) AT_KEYWORDS([m4@&t@_require m4@&t@_defun]) dnl From the m4sugar.m4 discourse: Require chains, top level AT_CHECK_M4SUGAR_TEXT([[dnl m4_defun([a], [[a]])dnl aka TEST2a m4_defun([b], [[b]m4_require([a])])dnl aka TEST3 m4_defun([c], [[c]m4_require([b])])dnl aka TEST2b m4_defun([d], [[d]m4_require([a])m4_require([c])])dnl aka TEST1 pre d d post ]], [[pre a b c d d post ]]) dnl From the m4sugar.m4 discourse: Require chains, nested AT_CHECK_M4SUGAR_TEXT([[dnl m4_defun([a], [[a]])dnl aka TEST2a m4_defun([b], [[b]m4_require([a])])dnl aka TEST3 m4_defun([c], [[c]m4_require([b])])dnl aka TEST2b m4_defun([d], [[d]m4_require([a])m4_require([c])])dnl aka TEST1 m4_defun([wrap], [pre d d post])dnl wrap ]], [[a b c pre d d post ]]) dnl Direct invocation, nested requires, top level AT_CHECK_M4SUGAR_TEXT([[dnl m4_defun([a], [[a]])dnl m4_defun([b], [[b]m4_require([a])])dnl m4_defun([c], [[c]m4_require([b])])dnl pre a c a c post ]], [[pre a b c a c post ]]) dnl Direct invocation, nested requires, nested defun. This is an example dnl of expansion before requirement, such that b occurs before its dnl prerequisite a. This indicates a bug in the macros (but not in dnl autoconf), so we should be emitting a warning. AT_CHECK_M4SUGAR_TEXT([[dnl m4_defun([a], [[a]])dnl m4_defun([b], [[b]m4_require([a])])dnl m4_defun([c], [[c]m4_require([b])])dnl dnl the extra macro layer works around line number differences in older m4 m4_define([foo], [m4_defun([outer], [pre a c a c post])])foo[]dnl outer ]], [[a b pre a c a c post ]], [[script.4s:15: warning: m4@&t@_require: 'a' was expanded before it was required script.4s:15: https://www.gnu.org/software/autoconf/manual/autoconf.html#Expanded-Before-Required script.4s:5: b is expanded from... script.4s:6: c is expanded from... script.4s:14: outer is expanded from... script.4s:15: the top level ]]) dnl Direct invocation, expand-before-require but no nested require. As this dnl is common in real life, but does not result in out-of-order expansion, dnl we silently permit this. AT_CHECK_M4SUGAR_TEXT([[dnl m4_defun([a], [[a]])dnl m4_defun([b], [[b]m4_require([a])])dnl m4_defun([c], [[c]])dnl m4_defun([d], [[d]m4_require([c])])dnl pre1 a b a b post1 m4_defun([outer], [pre2 c d c d post2])dnl outer m4_defun([e], [[e]])dnl m4_defun([f], [[f]m4_require([e])])dnl m4_defun([g], [[g] e f])dnl m4_defun([h], [[h]m4_require([g])])dnl h m4_defun([i], [[i]])dnl m4_defun([j], [[j] i])dnl m4_defun([k], [[k]m4_require([i])])dnl m4_defun([l], [[l]m4_require([k])])dnl m4_defun([m], [[m]m4_require([j])m4_require([l])])dnl m ]], [[pre1 a b a b post1 pre2 c d c d post2 g e f h j i k l m ]]) AT_CLEANUP ## ------------------------------------------------- ## ## m4_ifval, m4_ifblank, m4_ifset, m4_default, etc. ## ## ------------------------------------------------- ## AT_SETUP([m4sugar shorthand conditionals]) AT_KEYWORDS([m4@&t@_ifval m4@&t@_ifblank m4@&t@_ifnblank m4@&t@_ifset m4@&t@_default m4@&t@_default_quoted m4@&t@_default_nblank m4@&t@_default_nblank_quoted]) AT_CHECK_M4SUGAR_TEXT([[m4_define([active], [ACTIVE])m4_define([empty]) m4_ifval([active], [yes], [no]) m4_ifval([empty], [yes], [no]) m4_ifval([ ], [yes], [no]) m4_ifval([], [yes], [no]) m4_ifblank([active], [yes], [no]) m4_ifblank([empty], [yes], [no]) m4_ifblank([ ], [yes], [no]) m4_ifblank([], [yes], [no]) m4_ifnblank([active], [yes], [no]) m4_ifnblank([empty], [yes], [no]) m4_ifnblank([ ], [yes], [no]) m4_ifnblank([], [yes], [no]) m4_ifset([active], [yes], [no]) m4_ifset([empty], [yes], [no]) m4_ifset([ ], [yes], [no]) m4_ifset([], [yes], [no]) --- m4_define([demo1], [m4_default([$1], [$2])])dnl m4_define([demo2], [m4_default_quoted([$1], [$2])])dnl m4_define([demo3], [m4_default_nblank([$1], [$2])])dnl m4_define([demo4], [m4_default_nblank_quoted([$1], [$2])])dnl demo1([active], [default]) demo1([], [active]) demo1([empty], [text]) -demo1([ ], [active])- demo2([active], [default]) demo2([], [active]) demo2([empty], [text]) -demo2([ ], [active])- demo3([active], [default]) demo3([], [active]) demo3([empty], [text]) -demo3([ ], [active])- demo4([active], [default]) demo4([], [active]) demo4([empty], [text]) -demo4([ ], [active])- ]], [[ yes yes yes no no no yes yes yes yes no no yes no no no --- ACTIVE ACTIVE - - active active empty - - ACTIVE ACTIVE -ACTIVE- active active empty -active- ]]) AT_CLEANUP ## --------- ## ## m4_cond. ## ## --------- ## AT_SETUP([m4@&t@_cond]) AT_CHECK_M4SUGAR_TEXT([[m4_define([side], [m4_errprintn([$1])$1]) m4_cond([side(1)], [1], [a], [side(1)], [1], [b], [side(1)], [2], [c]) m4_cond([side(2)], [1], [a], [side(2)], [1], [b], [side(2)], [2], [c], [side(2)]) m4_cond([side(3)], [1], [a], [side(3)], [1], [b], [side(3)], [2], [c], [side(3)]) m4_cond([a,a], [a,a], [yes], [no]) m4_cond([[a,a]], [a,a], [yes]) m4_cond([a,a], [a,b], [yes], [no]) m4_cond([a,a], [a,b], [yes]) m4_cond([m4_eval([0xa])]) m4_define([ab], [AB])dnl m4_cond([a])b m4_cond([1], [1], [a])b m4_cond([1], [2], [3], [a])b ]], [[ a c 3 yes yes no 10 AB AB AB ]], [[1 2 2 2 3 3 3 3 ]]) AT_CLEANUP ## ---------- ## ## m4 lists. ## ## ---------- ## AT_SETUP([m4 lists]) AT_KEYWORDS([m4@&t@_car m4@&t@_cdr m4@&t@_argn _m4@&t_cdr]) AT_CHECK_M4SUGAR_TEXT([[dnl m4_define([a], [A])m4_define([b], [B])m4_define([c], [C]) m4_argn([1], [a], [b], [c]) m4_argn([2], [a], [b], [c]) m4_argn([3], [a], [b], [c]) m4_argn([4], [a], [b], [c]) m4_car([a], [b], [c]) m4_cdr([a], [b], [c]) m4_cdr([a], [b]) m4_cdr([a]) _m4_cdr([a], [b], [c]) _m4_cdr([a], [b]) _m4_cdr([a]) m4_if(m4_cdr([], []), [[]], [good], [bad]) m4_if(m4_cdr([]), [], [good], [bad]) ]], [[ a b c a [b],[c] [b] , [b],[c] , [b] good good ]]) AT_DATA_M4SUGAR([script.4s], [[m4_init m4_argn([0], [a], [b], [c]) ]]) AT_CHECK_M4SUGAR([-o-], [1], [], [[script.4s:2: error: assert failed: 0 < 0 script.4s:2: the top level autom4te: error: m4 failed with exit status: 1 ]]) AT_CLEANUP ## ---------- ## ## m4_split. ## ## ---------- ## AT_SETUP([m4@&t@_split]) AT_CHECK_M4SUGAR_TEXT( [[m4_define([active], [ACT, IVE])m4_define([bd], [oops]) m4_split m4_split([[]]) m4_split([ ]) m4_split([active]) m4_split([ active active ])end m4_split([ ], [ ]) m4_split([active], [ ]) m4_split([ active active ], [ ])end m4_split([abcde], [bd]) m4_split([abcde], [[bd]]) m4_split([foo=`` bar='']) m4_split([foo='' bar=``]) dnl these next two are from the manual; keep this in sync if the internal dnl quoting strings in m4_split are changed m4_define([a], [A])m4_define([b], [B])m4_define([c], [C])dnl m4_split([a )}>=- b -=<{( c]) m4_split([a )}@&t@>=- b -=<@&t@{( c]) ]], [[ [[]] [], [] [active] [], [active], [active], []end [], [] [active] [], [active active], []end [abcde] [a], [c], [e] [foo=``], [bar=''] [foo=''], [bar=``] [a], [], [B], [], [c] [a], [)}>=@&t@-], [b], [-@&t@=<{(], [c] ]]) AT_CLEANUP ## ------- ## ## m4_do. ## ## ------- ## AT_SETUP([m4@&t@_do]) AT_CHECK_M4SUGAR_TEXT( [[m4_define([ab], [1])m4_define([bc], [2])m4_define([abc], [3])dnl m4_define([AB], [4])m4_define([BC], [5])m4_define([ABC], [6])dnl m4_do m4_do([a]) m4_do([a], [b])c m4_unquote(m4_join([], [a], [b]))c m4_define([a], [A])m4_define([b], [B])m4_define([c], [C])dnl m4_do([a], [b])c m4_unquote(m4_join([], [a], [b]))c ]], [[ a abc 3 ABC 3 ]]) AT_CLEANUP ## ----------- ## ## m4_append. ## ## ----------- ## AT_SETUP([m4@&t@_append]) AT_KEYWORDS([m4@&t@_append_uniq m4@&t@_append_uniq_w]) AT_CHECK_M4SUGAR_TEXT( [[m4_define([active], [ACTIVE])dnl m4_append([sentence], [This is an])dnl m4_append([sentence], [ active ])dnl m4_append([sentence], [symbol.])dnl sentence m4_undefine([active])dnl sentence m4_define([active], [ACTIVE])dnl m4_append([hooks], [m4_define([act1], [act2])])dnl m4_append([hooks], [m4_define([act2], [active])])dnl m4_undefine([active])dnl act1 hooks act1 dnl Test for bug fixed in 2.62 when separator is active. m4_define([a], [A])dnl m4_append_uniq([foo], [-], [a])dnl m4_append_uniq([foo], [-], [a])dnl m4_append_uniq([bar], [-], [a])dnl m4_append_uniq([bar], [~], [a])dnl m4_append_uniq([bar], [-], [a])dnl m4_defn([foo]) m4_defn([bar]) foo bar m4_append_uniq([blah], [one], [, ], [new], [existing]) m4_append_uniq([blah], [two], [, ], [new], [existing]) m4_append_uniq([blah], [two], [, ], [new], [existing]) m4_append_uniq([blah], [three], [, ], [new], [existing]) m4_append([blah], [two], [, ])dnl blah m4_dquote(blah) m4_append([list], [one], [[, ]])dnl m4_append([list], [two], [[, ]])dnl m4_append([list], [three], [[, ]])dnl list m4_dquote(list) m4_append_uniq_w([numbers], [1 1 2])dnl m4_append_uniq_w([numbers], [ 2 3 ])dnl numbers ]], [[This is an ACTIVE symbol. This is an active symbol. act1 active - -a~ - -A~ new new existing new one, two, three, two [one],[two],[three],[two] one, two, three [one, two, three] 1 2 3 ]]) AT_DATA_M4SUGAR([script.4s], [[m4_init[]dnl m4_append_uniq([str], [a], [ ]) m4_append_uniq([str], [a b], [ ]) m4_divert([])dnl str ]]) AT_CHECK_M4SUGAR([-o-], 0, [[a a b ]], [[script.4s:3: warning: m4@&t@_append_uniq: 'a b' contains ' ' ]]) AT_CLEANUP ## --------- ## ## m4_join. ## ## --------- ## AT_SETUP([m4@&t@_join]) AT_KEYWORDS([m4@&t@_joinall]) AT_CHECK_M4SUGAR_TEXT( [[m4_define([active], [ACTIVE]) m4_join m4_join([|]) m4_join([, ], [one], [two]) m4_dquote(m4_join([, ], [one], [two])) m4_join([|], [active], [active]) m4_join([|], ,,,[one]) m4_join([|], [one],,,) m4_join([], ,,,[two]) m4_join([], [two],,,) m4_join([ active ], [one], , [two]) m4_join([], [one], [two]) m4_joinall([-], [one], [], [two]) m4_joinall([-], [], [], [three], [], []) m4_joinall([], [one], [], [two]) m4_joinall m4_joinall([-]) m4_joinall([-], [one]) ]], [[ one, two [one, two] active|active one one two two one active two onetwo one--two --three-- onetwo one ]]) AT_CLEANUP ## ----------- ## ## m4_expand. ## ## ----------- ## AT_SETUP([m4@&t@_expand]) AT_CHECK_M4SUGAR_TEXT( [[m4_define([active], [ACTIVE])dnl m4_expand([#active active]) m4_expand([[active]]) dnl properly quoted case statements m4_expand([case a in @%:@( *) echo active, ;; esac case b in *[)] echo active, ;; esac]) dnl unbalanced underquoted ')', but we manage anyway (gasp!) m4_expand([case c in #( *) echo active, ;; esac case d in *) echo active, ;; esac]) dnl unterminated comment/dnl m4_expand([active # active]) m4_expand([a dnl]) m4_expand([a -dnl]) ]], [[#active ACTIVE active case a in #( *) echo ACTIVE, ;; esac case b in *) echo ACTIVE, ;; esac case c in #( *) echo ACTIVE, ;; esac case d in *) echo ACTIVE, ;; esac ACTIVE # active a a - ]]) AT_CLEANUP ## ------------- ## ## m4_text_box. ## ## ------------- ## AT_SETUP([m4@&t@_text_box]) AT_CHECK_M4SUGAR_TEXT([[ m4_text_box([a $1 @&t@b]) m4_text_box([a $1 @&t@b], [$]) m4_text_box([a $1 @&t@b], [,]) ]], [[ ## ------ ## ## a $1 b ## ## ------ ## ## $$$$$$ ## ## a $1 b ## ## $$$$$$ ## ## ,,,,,, ## ## a $1 b ## ## ,,,,,, ## ]]) AT_CLEANUP ## -------------- ## ## m4_text_wrap. ## ## -------------- ## AT_SETUP([m4@&t@_text_wrap]) AT_KEYWORDS([m4@&t@_escape]) # m4_text_wrap is used to display the help strings. Also, check that # commas and $ are not swallowed. This can easily happen because of # m4-listification. AT_DATA_M4SUGAR([script.4s], [[m4_init[]m4_divert([])dnl m4_define([a], [OOPS])dnl m4_escape([a[b $c#]d]) m4_if(m4_escape([a[b $c#]d]), [a[b $c#]d], [oops], m4_escape([a[b $c#]d]), [a@<:@b @S|@c@%:@@:>@d], [pass], [oops]) m4_text_wrap([Short string */], [ ], [/* ], 20) m4_text_wrap([Much longer string */], [ ], [/* ], 20) m4_text_wrap([Short doc.], [ ], [ --short ], 30) m4_text_wrap([Short doc.], [ ], [ --too-wide], 30) m4_text_wrap([Super long documentation.], [ ], [ --too-wide], 30) m4_text_wrap([First, second , third, [,quoted space]]) m4_define([xfff], [oops]) m4_text_wrap([Some $1 $2 $3 $4 embedded dollars.], [ $* ], [ $@ ], [0xfff & 20]) ]]) AT_DATA([expout], [[a[b $c#]d pass /* Short string */ /* Much longer string */ --short Short doc. --too-wide Short doc. --too-wide Super long documentation. First, second , third, [,quoted space] $@ Some $1 $2 $3 $* $4 embedded $* dollars. ]]) AT_CHECK_M4SUGAR([-o-], 0, [expout]) AT_CLEANUP ## -------------------- ## ## m4_version_compare. ## ## -------------------- ## AT_SETUP([m4@&t@_version_compare]) AT_KEYWORDS([m4@&t@_list_cmp]) AT_CHECK_M4SUGAR_TEXT( [[m4_version_compare([1.1], [2.0]) m4_version_compare([2.0b], [2.0a]) m4_version_compare([2.0z], [2.0y]) m4_version_compare([1.1.1], [1.1.1a]) m4_version_compare([1.2], [1.1.1a]) m4_version_compare([1.0], [1]) m4_version_compare([1.0a], [1.0a]) m4_version_compare([1.1a], [1.1a.1]) m4_version_compare([1.10], [1.1a]) m4_version_compare([1-1a], [1,1A]) m4_define([a], [oops])dnl m4_version_compare([1.1a], [1.1A]) m4_version_compare([1z], [1aa]) m4_version_compare([2.61a], [2.61a-248-dc51]) m4_version_compare([2.61b], [2.61a-248-dc51]) m4_version_compare([08], [09]) m4_version_compare([010], [8]) dnl Test that side effects to m4_list_cmp occur exactly once m4_list_cmp([[0], [0], [0]m4_errprintn([hi])], [[0], [0], [0]m4_errprintn([hi])]) m4_list_cmp([[0], [0], [0]m4_errprintn([hi])], [[0], [0], [0]m4_errprintn([bye])]) ]], [[-1 1 1 -1 1 0 0 -1 1 0 0 -1 -1 1 -1 1 0 0 ]], [[hi hi hi bye ]]) AT_CLEANUP ## ------------------------------ ## ## Standard regular expressions. ## ## ------------------------------ ## AT_SETUP([Standard regular expressions]) # AT_CHECK_M4RE(RE-NAME, TEXT, INTENT = 'ok' | '') # ------------------------------------------------ # Check whether RE-NAME (a macro whose definition is a regular expression) # matches TEXT. INTENT = 'ok' if the match should succeed or else empty. m4_define([AT_CHECK_M4RE], [AT_CHECK_M4SUGAR_TEXT( [[m4_bregexp([$2], ^m4_defn([$1])$, [ok]) ]], [$3 ])]) AT_CHECK_M4RE([m4_re_word], [ab9_c], [ok]) AT_CHECK_M4RE([m4_re_word], [_9abc], [ok]) AT_CHECK_M4RE([m4_re_word], [9ab_c]) AT_CHECK_M4RE([m4_re_string], [ab9_c], [ok]) AT_CHECK_M4RE([m4_re_string], [_9abc], [ok]) AT_CHECK_M4RE([m4_re_string], [9ab_c], [ok]) AT_CHECK_M4RE([m4_re_string], [9a@_c]) AT_CLEANUP ## ----------- ## ## m4_bmatch. ## ## ----------- ## AT_SETUP([m4@&t@_bmatch]) AT_CHECK_M4SUGAR_TEXT( [[m4_bmatch([abc], [default\]) m4_bmatch([abc], [^a], [yes]) m4_bmatch([abc], [^a], [yes], [no]) m4_bmatch([abc], [^.a], [yes]) m4_bmatch([abc], [^.a], [yes], [no\]) m4_bmatch([abc], [a], [1], [b], [2]) m4_bmatch([abc], [A], [1], [b], [2]) m4_define([ab], [AB])dnl m4_bmatch([$*], [a])b m4_bmatch([$*], [\*], [a])b m4_bmatch([$*], [1], [2], [a])b ]], [[default\ yes yes no\ 1 2 AB AB AB ]]) AT_CLEANUP ## ------------------------ ## ## m4_toupper, m4_tolower. ## ## ------------------------ ## AT_SETUP([m4@&t@_toupper and m4@&t@_tolower]) AT_CHECK_M4SUGAR_TEXT( [[m4_define([abc], [hI])m4_define([ABC], [Hi]) m4_toupper(abc aBc ABC) m4_tolower(abc aBc ABC) m4_toupper([abc aBc ABC]) m4_tolower([abc aBc ABC]) m4_echo(m4_toupper(abc aBc ABC)) m4_echo(m4_tolower(abc aBc ABC)) m4_echo(m4_toupper([abc aBc ABC])) m4_echo(m4_tolower([abc aBc ABC])) m4_do(m4_toupper(abc aBc ABC)) m4_do(m4_tolower(abc aBc ABC)) m4_do(m4_toupper([abc aBc ABC])) m4_do(m4_tolower([abc aBc ABC])) ]], [[ HI ABC HI hi abc hi ABC ABC ABC abc abc abc HI ABC HI hi abc hi ABC ABC ABC abc abc abc HI Hi HI hi hI hi Hi Hi Hi hI hI hI ]]) AT_CLEANUP ## --------------- ## ## m4_bpatsubsts. ## ## --------------- ## AT_SETUP([m4@&t@_bpatsubsts]) AT_CHECK_M4SUGAR_TEXT( [[m4_bpatsubsts([11], [^..$]) m4_bpatsubsts([11], [\(.\)1], [\12]) m4_bpatsubsts([11], [^..$], [], [1], [2]) m4_bpatsubsts([11], [\(.\)1], [\12], [1], [3]) m4_define([a], [oops])m4_define([c], [oops])dnl m4_define([AB], [good])m4_define([bc], [good])dnl m4_bpatsubsts([abc], [a], [A], [b], [B], [c]) m4_bpatsubsts([ab], [a])c m4_bpatsubsts([ab], [c], [C], [a])c m4_bpatsubsts([$1$*$@], [\$\*], [$#]) ]], [[11 21 22 23 good good good $1$#$@ ]]) AT_CLEANUP ## -------------- ## ## m4_esyscmd_s. ## ## -------------- ## AT_SETUP([m4@&t@_esyscmd_s]) AT_KEYWORDS([m4@&t@_chomp m4@&t@_chomp_all]) AT_CHECK_M4SUGAR_TEXT( [[m4_define([world], [WORLD])dnl m4_chomp([abc]) m4_chomp([world ]) m4_esyscmd_s([echo hello world]) m4_esyscmd_s([echo '[goodbye, cruel world ]']) ]], [[abc world hello WORLD goodbye, cruel world ]]) AT_CLEANUP ## ---------- ## ## M4 Loops. ## ## ---------- ## AT_SETUP([M4 loops]) AT_KEYWORDS([m4@&t@_for m4@&t@_foreach m4@&t@_foreach_w m4@&t@_map_args_w]) AT_CHECK_M4SUGAR_TEXT([[dnl m4_define([myvar], [outer value])dnl m4_for([myvar], 1, 3, 1, [ myvar]) m4_for([myvar], 1, 3, , [ myvar]) m4_for([myvar], 3, 1,-1, [ myvar]) m4_for([myvar], 3, 1, , [ myvar]) m4_for([myvar], 1, 3, 2, [ myvar]) m4_for([myvar], 3, 1,-2, [ myvar]) m4_for([myvar],-1,-3,-2, [ myvar]) m4_for([myvar],-3,-1, 2, [ myvar]) dnl Make sure we recalculate the bounds correctly: m4_for([myvar], 1, 3, 3, [ myvar]) m4_for([myvar], 1, 6, 3, [ myvar]) m4_for([myvar],22,-7,-5, [ myvar]) m4_for([myvar],-2,-7,-4, [ myvar]) m4_for([myvar],-7,-2, 4, [ myvar]) dnl Make sure we are not exposed to division truncation: m4_for([myvar], 2, 5, 2, [ myvar]) m4_for([myvar],-5,-2, 2, [ myvar]) m4_for([myvar], 5, 2,-2, [ myvar]) m4_for([myvar],-2,-5,-2, [ myvar]) dnl Make sure we do not divide by zero: m4_for([myvar], 1, 1, , [ myvar]) m4_for([myvar], 1, 1,+2, [ myvar]) m4_for([myvar], 1, 1,-2, [ myvar]) dnl Make sure we do not loop endlessly m4_for([myval], 1, 1, 0, [ myval]) dnl Make sure to properly parenthesize m4_for([myvar], 3-5, -2+8, , [ myvar]) m4_for([myvar], -2+8, 3-5, , [ myvar]) m4_for([myvar], 8, 16, 3 * 2, [ myvar]) m4_for([myvar], 8, 16, -3 * -2, [ myvar]) m4_for([myvar], [2<<2], [2<<3], [-3 * (-2)], [ myvar]) dnl Modifying var does not affect the number of iterations m4_for([myvar], 1, 5, , [ myvar[]m4_define([myvar], 5)]) dnl Make sure we can do nameless iteration m4_for(, 1, 10, , -) dnl foreach tests m4_foreach([myvar], [[a], [b, c], [d], [e ],[f]], [ myvar|]) m4_foreach_w([myvar], [a b c, d,e f g], [ myvar|]) myvar m4_map_args_w([a b c, d,e f g], [ ], [|]) m4_map_args_w([a b], [\1], [/]) m4_define([dashes], [--])dnl m4_map_args_w([a b c], [/], [\1], [dashes]) dnl only one side effect expansion, prior to visiting list elements m4_foreach([i], [[1], [2], [3]m4_errprintn([hi])], [m4_errprintn(i)])dnl dnl shifting forms an important part of loops m4_shift3:m4_shift3(1,2,3):m4_shift3(1,2,3,4) m4_shiftn(3,1,2,3):m4_shiftn(3,1,2,3,4) ]], [[ 1 2 3 1 2 3 3 2 1 3 2 1 1 3 3 1 -1 -3 -3 -1 1 1 4 22 17 12 7 2 -3 -2 -6 -7 -3 2 4 -5 -3 5 3 -2 -4 1 1 1 1 -2 -1 0 1 2 3 4 5 6 6 5 4 3 2 1 0 -1 -2 8 14 8 14 8 14 1 2 3 4 5 ---------- a| b, c| d| e | f| a| b| c,| d,e| f| g| outer value a| b| c,| d,e| f| g| \1a/\1b/ /a\1--/b\1--/c\1 ::4 :4 ]], [[hi 1 2 3 ]]) dnl bounds checking in m4_for AT_DATA_M4SUGAR([script.4s], [[m4_init m4_divert([0])dnl m4_for([myvar], 1, 3,-1, [ myvar]) ]]) AT_CHECK_M4SUGAR([], 1, [], [[script.4s:3: error: assert failed: -1 > 0 script.4s:3: the top level autom4te: error: m4 failed with exit status: 1 ]]) AT_DATA_M4SUGAR([script.4s], [[m4_init m4_divert([0])dnl m4_for([myvar], 1, 2, 0, [ myvar]) ]]) AT_CHECK_M4SUGAR([], 1, [], [[script.4s:3: error: assert failed: 0 > 0 script.4s:3: the top level autom4te: error: m4 failed with exit status: 1 ]]) AT_DATA_M4SUGAR([script.4s], [[m4_init m4_divert([0])dnl m4_for([myvar], 2, 1, 0, [ myvar]) ]]) AT_CHECK_M4SUGAR([], 1, [], [[script.4s:3: error: assert failed: 0 < 0 script.4s:3: the top level autom4te: error: m4 failed with exit status: 1 ]]) dnl m4_shiftn also does bounds checking AT_DATA_M4SUGAR([script.4s], [[m4_init m4_divert([0])dnl m4_shiftn(3,1,2) ]]) AT_CHECK_M4SUGAR([], 1, [], [[script.4s:3: error: assert failed: 0 < 3 && 3 < 3 script.4s:3: the top level autom4te: error: m4 failed with exit status: 1 ]]) AT_CLEANUP ## --------------------- ## ## m4_map{,all}{,_sep}. ## ## --------------------- ## AT_SETUP([m4@&t@_map]) AT_KEYWORDS([m4@&t@_apply m4@&t@_map_sep m4@&t@_mapall m4@&t@_mapall_sep]) AT_KEYWORDS([m4@&t@_count]) AT_CHECK_M4SUGAR_TEXT([[dnl m4_map([m4_count], []) m4_map([ m4_count], [[], [[1]], [[1], [2]]]) m4_mapall([ m4_count], [[], [[1]], [[1], [2]]]) m4_map_sep([m4_eval], [,], [[[1+2]], [[10], [16]]]) m4_count(m4_map_sep([m4_echo], [,], [[], [[1]], [[2]]])) m4_count(m4_mapall_sep([m4_echo], [,], [[], [[1]], [[2]]])) m4_map_sep([m4_eval], [[,]], [[[1+2]], [[10], [16]]]) m4_count(m4_map_sep([m4_echo], [[,]], [[], [[1]], [[2]]])) m4_count(m4_mapall_sep([m4_echo], [[,]], [[], [[1]], [[2]]])) m4_map([-], [[]]) m4_mapall([-], [[]]) m4_map_sep([-], [:], [[]]) m4_mapall_sep([-], [:], [[]]) m4_define([a], [m4_if([$#], [0], [oops], [$1], [a], [pass], [oops])])dnl m4_define([a1], [oops])dnl m4_define([pass1], [oops])dnl m4_map([a], [[[a]]])1 m4_map([m4_unquote([a])], [m4_dquote([a])]) dnl only one side effect expansion, prior to visiting list elements m4_map([m4_errprintn], [[[1]], [[2]], [[3]]m4_errprintn([hi])])dnl m4_map_sep([m4_errprintn], [], [[[1]], [[2]], [[3]]m4_errprintn([hi])])dnl m4_mapall([m4_errprintn], [[[1]], [[2]], [[3]]m4_errprintn([hi])])dnl m4_mapall_sep([m4_errprintn], [], [[[1]], [[2]], [[3]]m4_errprintn([hi])])dnl ]], [[ 1 2 0 1 2 3,a 2 3 3,a 1 1 - - pass1 pass ]], [[hi 1 2 3 hi 1 2 3 hi 1 2 3 hi 1 2 3 ]]) AT_CLEANUP ## --------------------------------------- ## ## m4_map_args{,_sep,_pair} and m4_curry. ## ## --------------------------------------- ## AT_SETUP([m4@&t@_map_args and m4@&t@_curry]) AT_KEYWORDS([m4@&t@_map_args_sep m4@&t@_map_args_pair m4@&t@_reverse m4@&t@_map]) dnl First, make sure we can curry in isolation. AT_CHECK_M4SUGAR_TEXT( [[m4_curry([m4_echo])([1]) m4_curry([m4_curry], [m4_reverse], [1])([2])([3]) m4_define([add], [m4_eval(([$1]) + ([$2]))])dnl m4_define([add_one], [m4_curry([add], [1])])dnl add_one()([4]) ]], [[1 3, 2, 1 5 ]]) dnl Now, check that we can map a list of arguments. AT_CHECK_M4SUGAR_TEXT([[m4_define([active], [ACTIVE])dnl m4_map_args([ m4_echo]) m4_map_args([ m4_echo], [plain], [active]) m4_map_args([m4_unquote], [plain], [active]) m4_map_args_pair([, m4_reverse], []) m4_map_args_pair([, m4_reverse], [], [1]) m4_map_args_pair([, m4_reverse], [], [1], [2]) m4_map_args_pair([, m4_reverse], [], [1], [2], [3]) m4_map_args_pair([, m4_reverse], [], [1], [2], [3], [4]) m4_map_args_pair([, m4_reverse], [, m4_dquote], [1]) m4_map_args_pair([, m4_reverse], [, m4_dquote], [1], [2]) m4_map_args_pair([, m4_reverse], [, m4_dquote], [1], [2], [3]) m4_map_args_pair([, m4_reverse], [, m4_dquote], [1], [2], [3], [4]) m4_map_args_sep([<], [>], [:], [1], [2], [3]) m4_map_args_sep([m4_echo(], [)], [ ], [plain], [active]) ]], [[ plain active plainACTIVE , 1 , 2, 1 , 2, 1, 3 , 2, 1, 4, 3 , [1] , 2, 1 , 2, 1, [3] , 2, 1, 4, 3 <1>:<2>:<3> plain active ]]) dnl Finally, put the two concepts together, to show the real power of the API. AT_CHECK_M4SUGAR_TEXT( [[m4_define([add], [m4_eval(([$1]) + ([$2]))])dnl m4_define([list], [[-1], [0], [1]])dnl dnl list_add_n(value, arg...) dnl add VALUE to each ARG and output the resulting list m4_define([list_add_n], [m4_shift(m4_map_args([,m4_curry([add], [$1])], m4_shift($@)))]) list_add_n([1], list) list_add_n([2], list) ]], [[ 0,1,2 1,2,3 ]]) AT_CLEANUP ## ------------ ## ## m4_combine. ## ## ------------ ## AT_SETUP([m4@&t@_combine]) AT_CHECK_M4SUGAR_TEXT([[m4_define([a], [oops])dnl m4_combine([, ], [[a], [b], [c]], [-], [1], [2], [3]) m4_combine([, ], [[a], [b]], [-]) m4_combine([, ], [[a], [b]], [-], []) m4_combine([, ], [], [-], [a], [b]) m4_combine([, ], [[]], [-], [a], [b]) m4_combine([ a ], [[-], [+]], [a], [-], [+]) m4_combine([$* ], [[$1], [$2]], [$#], [$@]) ]], [[a-1, a-2, a-3, b-1, b-2, b-3, c-1, c-2, c-3 a-, b- -a, -b -a- a -a+ a +a- a +a+ $1$#$@$* $2$#$@ ]], []) AT_CLEANUP ## -------------- ## ## m4_{max,min}. ## ## -------------- ## AT_SETUP([m4@&t@_max and m4@&t@_min]) AT_DATA_M4SUGAR([script.4s], [[m4_max ]]) AT_CHECK_M4SUGAR([], 1, [], [[script.4s:1: error: too few arguments to m4@&t@_max script.4s:1: the top level autom4te: error: m4 failed with exit status: 1 ]]) AT_DATA_M4SUGAR([script.4s], [[m4_min ]]) AT_CHECK_M4SUGAR([], 1, [], [[script.4s:1: error: too few arguments to m4@&t@_min script.4s:1: the top level autom4te: error: m4 failed with exit status: 1 ]]) AT_CHECK_M4SUGAR_TEXT([[dnl m4_min(0) m4_min(0xa) m4_min(0, 0) m4_min(0, 1) m4_min(1, 0) m4_min(0+1, 1+1) m4_min(0+1, 1+0) m4_min(0, 1, 2) m4_min(2, 1, 0) m4_min(1m4_for([i], 2, 100, , [,i])) m4_min(m4_for([i], 100, 2, , [i,])1) ---- m4_max(0) m4_max(0xa) m4_max(0, 0) m4_max(0, 1) m4_max(1, 0) m4_max(1+0, 1+1) m4_max(1+0, 1+0) m4_max(0, 1, 2) m4_max(2, 1, 0) m4_max(1m4_for([i], 2, 100, , [,i])) m4_max(m4_for([i], 100, 2, , [i,])1) ]], [[0 10 0 0 0 1 1 0 0 1 1 ---- 0 10 0 1 1 2 1 2 2 100 100 ]], []) AT_CLEANUP ## ----------- ## ## Recursion. ## ## ----------- ## AT_SETUP([recursion]) AT_KEYWORDS([m4@&t@_foreach m4@&t@_foreach_w m4@&t@_case m4@&t@_cond m4@&t@_bpatsubsts m4@&t@_shiftn m4@&t@_do m4@&t@_dquote_elt m4@&t@_reverse m4@&t@_map m4@&t@_join m4@&t@_joinall m4@&t@_list_cmp m4@&t@_max m4@&t@_min m4@&t@_bmatch m4@&t@_map_args m4@&t@_map_args_pair]) dnl This test completes in a reasonable time if m4_foreach is linear, dnl but thrashes if it is quadratic. If we are testing with m4 1.4.x, dnl only the slower foreach.m4 implementation will work. But if we dnl are testing with m4 1.6, we can rerun the test with __m4_version__ dnl undefined to exercise the alternate code path. AT_DATA_M4SUGAR([script.4s], [[m4_init m4_divert_push([])[]dnl m4_len(m4_foreach_w([j], m4_do(m4_for([i], [1], [10000], [], [,i ])), [j ])) m4_shiftn(9998m4_for([i], [1], [10000], [], [,i])) m4_len(m4_join([--],, m4_dquote_elt(m4_for([i], [1], [10000], [], [,i])),)) m4_len(m4_joinall([--], m4_map([, m4_echo], m4_dquote([1]m4_for([i], [2], [10000], [], [,i]))))) m4_max(m4_min([1]m4_for([i], [2], [10000], [], [,i]))m4_for([i], [2], [10000], [], [,i])) m4_case([10000]m4_for([i], [1], [10000], [], [,i]),[end]) m4_list_cmp(m4_dquote(1m4_for([i], [2], [10000], [], [,i])), m4_dquote(m4_reverse(10000m4_for([i], [9999], [1], [], [,i])), [0])) m4_list_cmp([0], [0m4_for([i], [1], [10000], [], [,0])]) m4_list_cmp([0m4_for([i], [1], [10000], [], [,0])], [0]) m4_for([i], [1], [10000], [], [m4_define(i)])dnl m4_undefine(1m4_for([i], [2], [10000], [], [,i]))dnl m4_bpatsubsts([a1]m4_for([i], [1], [10000], [], [,i]), [a2], [A]) m4_bmatch([9997]m4_for([i], [1], [10000], [], [,^i$])) m4_define([up], [m4_define([$1], m4_incr($1))$1])m4_define([j], 0)dnl m4_cond(m4_for([i], [1], [10000], [], [[up([j])], [9990], i,]) [oops]) j m4_count(m4_map_args_pair([,m4_quote], []m4_map_args([,m4_echo]m4_for([i], [1], [10000], [], [,i])))) m4_divert_pop([]) ]]) AT_CHECK_M4SUGAR([-o-], [0], [[48894 9999,10000 78896 58894 10000 end 0 0 0 A ^9998$ 9990 9990 5001 ]]) AT_DATA_M4SUGAR([script.4s], [[m4_ifdef([__m4_version__], [m4_undefine([__m4_version__])], [m4_divert_push([])48894 9999,10000 78896 58894 10000 end 0 0 0 A ^9998$ 9990 9990 5001 m4_exit([0])]) m4_init m4_divert_push([])[]dnl m4_len(m4_foreach_w([j], m4_do(m4_for([i], [1], [10000], [], [,i ])), [j ])) m4_shiftn(9998m4_for([i], [1], [10000], [], [,i])) m4_len(m4_join([--],, m4_dquote_elt(m4_for([i], [1], [10000], [], [,i])),)) m4_len(m4_joinall([--], m4_map([, m4_echo], m4_dquote([1]m4_for([i], [2], [10000], [], [,i]))))) m4_max(m4_min([1]m4_for([i], [2], [10000], [], [,i]))m4_for([i], [2], [10000], [], [,i])) m4_case([10000]m4_for([i], [1], [10000], [], [,i]),[end]) m4_list_cmp(m4_dquote(1m4_for([i], [2], [10000], [], [,i])), m4_dquote(m4_reverse(10000m4_for([i], [9999], [1], [], [,i])), [0])) m4_list_cmp([0], [0m4_for([i], [1], [10000], [], [,0])]) m4_list_cmp([0m4_for([i], [1], [10000], [], [,0])], [0]) m4_for([i], [1], [10000], [], [m4_define(i)])dnl m4_undefine(1m4_for([i], [2], [10000], [], [,i]))dnl m4_bpatsubsts([a1]m4_for([i], [1], [10000], [], [,i]), [a2], [A]) m4_bmatch([9997]m4_for([i], [1], [10000], [], [,^i$])) m4_define([up], [m4_define([$1], m4_incr($1))$1])m4_define([j], 0)dnl m4_cond(m4_for([i], [1], [10000], [], [[up([j])], [9990], i,]) [oops]) j m4_count(m4_map_args_pair([,m4_quote], []m4_map_args([,m4_echo]m4_for([i], [1], [10000], [], [,i])))) m4_divert_pop([]) ]]) AT_CHECK_M4SUGAR([-o-], [0], [[48894 9999,10000 78896 58894 10000 end 0 0 0 A ^9998$ 9990 9990 5001 ]]) AT_CLEANUP ## ---------- ## ## m4_set_*. ## ## ---------- ## AT_SETUP([m4@&t@_set]) AT_KEYWORDS([m4@&t@_set_add m4@&t@_set_add_all m4@&t@_set_contains m4@&t@_set_contents m4@&t@_set_delete m4@&t@_set_difference m4@&t@_set_dump m4@&t@_set_empty m4@&t@_set_foreach m4@&t@_set_intersection m4@&t@_set_list m4@&t@_set_listc m4@&t@_set_map m4@&t@_set_remove m4@&t@_set_size m4@&t@_set_union]) # Simple tests AT_CHECK_M4SUGAR_TEXT([[m4_set_contains([a], [1], [yes], [no]) m4_set_add([a], [1], [added], [dup]) m4_set_contains([a], [1], [yes], [no]) m4_set_add([a], [1], [added], [dup]) m4_set_contents([a]) m4_set_remove([a], [1], [removed], [missing]) m4_set_contains([a], [1], [yes], [no]) m4_set_remove([a], [1], [removed], [missing]) m4_set_add([a], [2], [added], [dup]) m4_set_empty([a], [yes], [no]) m4_set_delete([a]) m4_set_empty([a], [yes], [no]) m4_set_add_all([c], [1], [2], [3]) m4_set_add_all([a]m4_set_listc([c])) m4_set_contents([c], [-]) m4_set_dump([a], [-]) m4_set_contents([a]) m4_set_add_all([a], [1], [2], [3])m4_set_add_all([b], [3], [], [4]) m4_set_difference([a], [b]) m4_set_difference([b], [a]) m4_set_intersection([a], [b]) m4_set_union([a], [b]) m4_define([printodd], [m4_if(m4_eval([$1 & 1]), [1], [:$1])])dnl m4_set_map([a], [printodd]) m4_set_foreach([a], [i], [m4_if(m4_eval(i & 1), [1], [m4_set_remove([a], i)])]) m4_set_list([a]) m4_set_add([a], []) m4_set_list([a]) m4_set_remove([a], [2]) m4_dquote(m4_set_list([a])) m4_set_listc([a]) m4_set_size([a]) m4_set_delete([a]) m4_dquote(m4_set_list([a])) m4_indir([m4_dquote]m4_set_listc([a])) m4_set_listc([a]) m4_set_size([a]) ]], [[no added yes dup 1 removed no missing added no yes 1-2-3 3-2-1 ,1,2 ,,4 ,3 ,1,2,3,,4 :1:3 2 2, [] , 1 [] 0 ]]) # Stress tests - check for unusual names/values AT_CHECK_M4SUGAR_TEXT([[m4_define([a], [oops])dnl m4_set_add([a], [a])dnl m4_set_remove([a], [oops], [yes], [no]) m4_set_add([a,b], [c])dnl m4_set_add([a,b], [$*[]])dnl m4_set_add_all([a], [b,c])dnl m4_set_size([a]) m4_count(m4_set_contents([a], [,])) m4_count(m4_set_list([a], [,])) m4_set_dump([a], [,]) m4_set_contents([a,b], [,]) m4_set_list([a,b]) m4_set_foreach([$*[]], [$*[]], [oops]) m4_set_add([$*[]], [])dnl m4_set_remove([$*[]], [a], [yes], [no]) m4_set_add([$*[]], [a])dnl m4_set_foreach([$*[]], [$*[]], [-m4_defn([$*[]])m4_indir([$*[]])-]) m4_set_remove([$*[]], [], [yes], [no]) m4_set_add([c], [,])dnl m4_set_foreach([a,b], [set], [:m4_set_listc(_m4_defn([set])):]) ]],[[no 2 1 2 b,c,a c,$*[] c,$*[] no ---aoops- yes :,,::,a: ]]) # Stress tests - check for linear scaling (won't necessarily fail if # quadratic, but hopefully users will complain if it appears to hang) AT_CHECK_M4SUGAR_TEXT([[dnl m4_for([i], [1], [10000], [], [m4_set_add([a], i)])dnl m4_set_add_all([b]m4_for([i], [1], [10000], [], [,i]))dnl m4_set_remove([a], [1])dnl m4_set_remove([b], [10000])dnl m4_set_add_all([a]m4_for([i], [1], [10000], [], [,i]))dnl m4_for([i], [1], [10000], [], [m4_set_add([b], i)])dnl m4_len(m4_set_contents([a])) m4_len(m4_set_foreach([b], [b], [m4_if(m4_eval(b & 1), [1], [m4_set_remove([b], b, [-])])])) m4_set_size([b]) m4_define([prune3x], [m4_if(m4_eval([$1 % 3]), [0], [m4_set_remove([a], [$1], [-])])])dnl m4_len(m4_set_map([a], [prune3x])) m4_count(m4_shift(m4_set_intersection([a], [b]))) ]], [[38894 5000 5000 3333 3334 ]]) AT_CLEANUP ## ---------------------- ## ## __file__ and __line__. ## ## ---------------------- ## AT_SETUP([[__file__ and __line__]]) # Check that __file__ and __line__ work. # Check that m4__file__ and m4__line__ are not defined # (and get them to pass by the undefined-macro check). # Try to not assume too much about AT_CHECK_M4SUGAR_TEXT. AT_CHECK_M4SUGAR_TEXT([[dnl m4_pattern_allow([m4__file__])dnl m4_pattern_allow([m4__line__])dnl m4__file__ m4__line__ __file__ m4_define([first], __line__)dnl m4_define([second], __line__)dnl m4_assert(first + 1 == second)dnl ]], [[m4@&t@__@&t@file__ m4@&t@__@&t@line__ script.4s ]]) AT_CLEANUP