#
# _m4_foreach to the rescue. If no deletions have occurred, then
# avoid the speed penalty of m4_set_add.
-m4_define([m4_set_add_all],
-[m4_if([$#], [0], [], [$#], [1], [],
- [m4_define([_m4_set_size($1)], m4_eval(m4_set_size([$1])
- + m4_len(_m4_foreach(m4_ifdef([_m4_set_cleanup($1)],
- [[m4_set_add]], [[_$0]])[([$1],], [)], $@))))])])
-
-m4_define([_m4_set_add_all],
-[m4_ifdef([_m4_set([$1],$2)], [],
- [m4_define([_m4_set([$1],$2)],
- [1])m4_pushdef([_m4_set([$1])], [$2])-])])
+m4_define([_m4_set_add_all_clean],
+[m4_if([$#], [2], [],
+ [_m4_foreach([_m4_set_add_clean([$1],], [, [-])], m4_shift($@))])])
+
+m4_define([_m4_set_add_all_check],
+[m4_if([$#], [2], [],
+ [_m4_foreach([_m4_set_add([$1],], [, [-])], m4_shift($@))])])
# supply the value via _m4_defn([_m4_set([name])]) without needing any
# quote manipulation.
+
+# _m4_set_add(SET, VALUE, [IF-UNIQ], [IF-DUP])
+# --------------------------------------------
+# Subroutine of m4_set_add and m4_set_add_all.
+# Add VALUE as an element of SET, but do not update the size of SET.
+# Expand IF-UNIQ on the first addition, and IF-DUP if it is already in
+# the set.
+#
+# Three cases must be handled:
+# - _m4_set([$1],$2) is not defined:
+# define _m4_set([$1],$2) to 1, push $2 as a definition of _m4_set([$1]),
+# expand IF-UNIQ.
+# - _m4_set([$1],$2) is defined with value 0:
+# define _m4_set([$1],$2) to 1, *don't* modify _m4_set([$1]),
+# expand IF-UNIQ.
+# - _m4_set([$1],$2) is defined with value 1:
+# do nothing but expand IF-DUP.
+m4_define([_m4_set_add],
+[m4_ifndef([_m4_set([$1],$2)],
+ [m4_pushdef([_m4_set([$1])],[$2])m4_define([_m4_set([$1],$2)],[1])$3],
+ [m4_if(m4_indir([_m4_set([$1],$2)]), [0],
+ [m4_define([_m4_set([$1],$2)],[1])$3],
+ [$4])])])
+
+# _m4_set_add_clean(SET, VALUE, [IF-UNIQ], [IF-DUP])
+# --------------------------------------------------
+# Subroutine of m4_set_add_all.
+# Add VALUE as an element of SET, but do not update the size of SET.
+# It is safe to assume that VALUE is not a tombstone, i.e. either
+# _m4_set([$1],$2) is not defined or it is defined with value 1.
+# Expand IF-UNIQ on the first addition, and IF-DUP if it is already in
+# the set.
+m4_define([_m4_set_add_clean],
+[m4_ifndef([_m4_set([$1],$2)],
+ [m4_pushdef([_m4_set([$1])],[$2])m4_define([_m4_set([$1],$2)],[1])$3],
+ [$4])])
+
# m4_set_add(SET, VALUE, [IF-UNIQ], [IF-DUP])
# -------------------------------------------
# Add VALUE as an element of SET. Expand IF-UNIQ on the first
# unpruned element, but it is just as easy to check existence directly
# as it is to query _m4_set_cleanup($1).
m4_define([m4_set_add],
-[m4_ifdef([_m4_set([$1],$2)],
- [m4_if(m4_indir([_m4_set([$1],$2)]), [0],
- [m4_define([_m4_set([$1],$2)],
- [1])_m4_set_size([$1], [m4_incr])$3], [$4])],
- [m4_define([_m4_set([$1],$2)],
- [1])m4_pushdef([_m4_set([$1])],
- [$2])_m4_set_size([$1], [m4_incr])$3])])
+[_m4_set_add([$1], [$2], [_m4_set_size([$1], [m4_incr])$3], [$4])])
# m4_set_add_all(SET, VALUE...)
# -----------------------------
#
# Please keep foreach.m4 in sync with any adjustments made here.
m4_define([m4_set_add_all],
-[m4_define([_m4_set_size($1)], m4_eval(m4_set_size([$1])
- + m4_len(m4_ifdef([_m4_set_cleanup($1)], [_$0_check], [_$0])([$1], $@))))])
+[m4_case([$#], [0], [], [1], [],
+ [m4_define([_m4_set_size($1)],
+ m4_eval(m4_set_size([$1])
+ + m4_len(m4_ifdef([_m4_set_cleanup($1)],
+ [_$0_check], [_$0_clean])([$1], $@))))])])
-m4_define([_m4_set_add_all],
+m4_define([_m4_set_add_all_clean],
[m4_if([$#], [2], [],
- [m4_ifdef([_m4_set([$1],$3)], [],
- [m4_define([_m4_set([$1],$3)], [1])m4_pushdef([_m4_set([$1])],
- [$3])-])$0([$1], m4_shift2($@))])])
+ [_m4_set_add_clean([$1], [$3], [-], [])$0([$1], m4_shift2($@))])])
m4_define([_m4_set_add_all_check],
[m4_if([$#], [2], [],
- [m4_set_add([$1], [$3])$0([$1], m4_shift2($@))])])
+ [_m4_set_add([$1], [$3], [-], [])$0([$1], m4_shift2($@))])])
# m4_set_contains(SET, VALUE, [IF-PRESENT], [IF-ABSENT])
# ------------------------------------------------------
3334
]])
+# Implementation corner cases.
+
+# m4_set_add_all dispatches to one of two different helper macros
+# depending on whether the set has any tombstones; verify their
+# effects are equivalent. N.B. m4_set_dump, m4_set_list, etc. produce
+# elements in an arbitrary order, so this test is more brittle than
+# I'd like.
+AT_CHECK_M4SUGAR_TEXT([[dnl
+m4_define([echo_if_member], [m4_set_contains([$1], [$2], [$2])])dnl
+m4_set_add_all([a], [1])dnl
+m4_set_add_all([a], [2], [3])dnl
+m4_set_add_all([a], [4], [5], [6])dnl
+m4_set_size([a])
+m4_map_args_sep([echo_if_member([a],], [)], [,],
+ [1], [2], [3], [4], [5], [6], [x], [y])
+m4_set_dump([a], [,])
+
+m4_set_add([b], [x])dnl
+m4_set_add([b], [y])dnl
+m4_set_remove([b], [x])dnl
+m4_set_add_all([b], [1])dnl
+m4_set_add_all([b], [2], [3])dnl
+m4_set_add_all([b], [4], [5], [6])dnl
+m4_set_remove([b], [y])dnl
+m4_set_size([b])
+m4_map_args_sep([echo_if_member([b],], [)], [,],
+ [1], [2], [3], [4], [5], [6], [x], [y])
+m4_set_dump([b], [,])
+
+m4_set_add([c], [x])dnl
+m4_set_add([c], [y])dnl
+m4_set_remove([c], [y])dnl
+m4_set_add_all([c], [1])dnl
+m4_set_add_all([c], [2], [3])dnl
+m4_set_add_all([c], [4], [5], [6])dnl
+m4_set_remove([c], [x])dnl
+m4_set_size([c])
+m4_map_args_sep([echo_if_member([c],], [)], [,],
+ [1], [2], [3], [4], [5], [6], [x], [y])
+m4_set_dump([c], [,])
+]], [[6
+1,2,3,4,5,6,,
+6,5,4,3,2,1
+
+6
+1,2,3,4,5,6,,
+6,5,4,3,2,1
+
+6
+1,2,3,4,5,6,,
+6,5,4,3,2,1
+]])
+
AT_CLEANUP