The following macros are very general-purpose. In the webscripts.tgz package, these macros are to be found in the file include/ext.m4.
Here's a simple macro to count the number of elements in a comma-separated list:
define(`count', ``$#'')dnl
Very often, you will need to shift a large number of
arguments out of the way before passing the rest onto the
next macro. The built-in shift
macro shifts just one argument; the macro below shifts the
number given by its first argument:
define(`shiftn_pos',`ifelse(eval(`$1<1'),0, `shiftn_pos(eval(`$1-1'),shift(shift($@)))', `shift($@)')')dnl define(`shiftn',`ifelse(eval(`$1>=0'),`0', `shiftn_pos(eval(count($@)`-1+$1'),shift($@))', `shiftn_pos($@)')')dnl
So:
shiftn(`2',``a'',``b'',``c'',``d'',``e'',``f'')
…expands to:
`c',`d',`e',`f'
A negative argument shifts the complement of the length, so:
shiftn(`-2',``a'',``b'',``c'',``d'',``e'',``f'')
…expands to:
`e',`f'
This macro complements shiftn:
define(`keepn_pos', `ifelse(eval(`$1<1'),0, ``$2'`'ifelse(eval(`$1<2'),0,`,')`'keepn_pos(eval(`$1-1'), shift(shift($@)))')')dnl dnl define(`keepn',`ifelse(eval(`$1>=0'),`0', `keepn_pos(eval(count($@)`-1+$1'),shift($@))', `keepn_pos($@)')')dnl
The first argument specifies how many of the remaining arguments should be retained. So:
keepn(`2',``a'',``b'',``c'',``d'',``e'',``f'')
…expands to:
`a',`b'
A negative argument keeps the complement of the length, so:
keepn(`-2',``a'',``b'',``c'',``d'',``e'',``f'')
…expands to:
`a',`b',`c',`d'
The macro split splits its fifth
argument up using the separator given in its first. It
passes each ‘word’, quoted the number of times specified by
the third argument, and followed by all arguments after the
fifth, to the macro named in the second argument . The
resultant expansions are concatenated with the fourth
argument as a separator. rsplit is
similar, but the separator used to split the string is
treated as a regular expression.
define(`x_split',`ifelse( indir(`$1',`$6',`$2'),`-1',`indir(`$3',`$6',shiftn(`6',$@))', `indir(`$3',substr(`$6',`0',indir(`$1',`$6',`$2'), ifelse($4,,``1'',`eval(`$4+1')')), shiftn(`6',$@))`'$5`'x_split(`$1',`$2',`$3',`$4',`$5', substr(`$6',eval(indir(`$1',`$6',`$2')`+1'),,`1'),shiftn(`6',$@))')')dnl dnl define(`split',`x_split(`index',$@)')dnl define(`rsplit',`x_split(`regexp',$@)')dnl
This macro extracts a sublist from a list:
define(`select',`keepn(`$2',shiftn(`$1',shift(shift($@))))')dnl
The first argument specifies how many of the initial arguments should be discarded. The second argument specifies how many of the remaining arguments should be retained. For example:
select(`2',`3',``a'',``b'',``c'',``d'',``e'',``f'')
…expands to:
`c',`d',`e'
This macro removes the core of a list, complementing
select:
define(`reject',`keepn(`$1', shift(shift($@))),shiftn(eval(`$1+$2'),shift(shift($@)))')dnl
The first argument specifies how many initial arguments should be retained. The second argument specifies how many of the following arguments should be dropped. For example:
reject(`2',`3',``a'',``b'',``c'',``d'',``e'',``f'')
…expands to:
`a',`b',`f'
Sometimes you need to strip away a pair of quotes returned by another macro. This macro causes a further evaluation of its argument to do that:
define(`dequote',$*)dnl
If we want to dequote more than once, we use this:
define(`dequoten',`ifelse(eval(`$1<1'),`0', `dequote(dequoten(eval(`$1-1'),shift($@)))',`shift($@)')')dnl
So:
define(`list',```a'',``b'',``c'',``d'',``e'',``f''')dnl define(`c',``sea'')dnl define(`sea',``see'')dnl dnl dequoten(`1',list) dequoten(`2',list) dequoten(`3',list)
…expands to:
a,b,c,d,e,f a,b,sea,d,e,f a,b,see,d,e,f
This macro requotes the rest of its arguments the number of times specified by its first:
define(`quoten',`substr($2,`0',,eval(`$1+2'))ifelse(`$#',`0',, `$#',`1',, `$#',`2',, `,quoten(`$1',shift(shift($@)))')')dnl
So:
define(`list',```a'',``b'',``c'',``d'',``e'',``f''')dnl dnl quoten(`1',list) quoten(`2',list) quoten(`3',list)
…expands to:
``a'',``b'',``c'',``d'',``e'',``f'' ```a''',```b''',```c''',```d''',```e''',```f''' ````a'''',````b'''',````c'''',````d'''',````e'''',````f''''
Here is the short version for adding only a single pair of quotes:
define(`quote',`quoten(`1',$@)')dnl
The macro selects is a variation of
select — its first argument
describes the region to be selected:
define(`selects_1',`ifelse( eval(`-1!='regexp(`$1',`\([+-]?[0-9]+\)\.\.\([+-]?[0-9]+\)')),1, `regexp(`$1',`\([+-]?[0-9]+\)\.\.\([+-]?[0-9]+\)', `select(`\1',`\2',shift($@))')', eval(`-1!='regexp(`$1',`\.\.\([+-]?[0-9]+\)')),1, `regexp(`$1',`\.\.\([+-]?[0-9]+\)',`keepn(`\1',shift($@))')', eval(`-1!='regexp(`$1',`\([+-]?[0-9]+\)\.\.')),1, `regexp(`$1',`\([+-]?[0-9]+\)\.\.',`shiftn(`\1',shift($@))')', eval(`-1!='regexp(`$1',`\([+-]?[0-9]+\)')),1, `regexp(`$1',`\([+-]?[0-9]+\)',`select(`\1',1,shift($@))')')')dnl define(`selects',`split(`,',`selects_1',,`,',$@)')dnl
For example:
selects(`2..3',``a'',``b'',``c'',``d'',``e'',``f'')
…expands to:
`c',`d',`e'
But selects is more powerful than
that:
selects(`3..2,-4,1',``a'',``b'',``c'',``d'',``e'',``f'')
…expands to:
`d',`e',`c',`b'
Like selects, the macro mselects accepts a selection string as its
first argument. However, each subsequent argument is a list
from which we want to select some elements to form a new
list. In the selection string, each group of selections is
preceded by the number of the list to select from.
Selections from different lists are separated by
semicolons.
define(`mselects_1',`regexp(`$1',`\([0-9]+\):\(.*\)', `selects(`\2',dequote(select(`\1',`1',shift($@))))')')dnl define(`mselects',`split(`;',`mselects_1',,`,',$@)')dnl
For example:
mselects(`1:3..2,-4,1;0:2..4,0', ```A'',``B'',``C'',``D'',``E'',``F'',``G'',``H''', ```a'',``b'',``c'',``d'',``e'',``f''')
…expands to:
`d',`e',`c',`b',`C',`D',`E',`F',`A'
The built-in macro index gives us
the position of the first occurance of one string in
another. Here's a macro that does the same thing with a
list:
define(`search_2', `ifelse(ifelse(`$2',,`ifelse(`$3',`$4',``1'',``0'')', `indir(`$2', `$3', `$4')'),`0', dequote(ifelse(`$#',`4',```-1''', ``search_1(incr($1),reject(`2',`1',shift($@)))'')), `$1')')dnl dnl define(`search_1', `ifelse(`$#',`0',``-1'', `$#',`1',``-1'', `$#',`2',``-1'', `$#',`3',``-1'', `search_2($@)')')dnl dnl define(`search',`search_1(``0'',$@)')dnl
The second argument of search is the sought string; the arguments
after it are searched. The result is the matching
argument's index, minus 2. For example:
search(,`d',`a',`b',`c',`d',`e',`f',`g') search(,`d',`A',`B',`C',`D',`E',`F',`G')
…expands to:
3 -1
The first argument, if not blank, is the name of a macro to
be used to match the target string with each one of the
listed strings. It should expand to 0 if it fails to match. Another example:
define(`caseless_equals',`ifelse(toupper(`$1'),toupper(`$2'),`1',`0')')dnl search(`caseless_equals',`d',`a',`b',`c',`d',`e',`f',`g') search(`caseless_equals',`d',`A',`B',`C',`D',`E',`F',`G')
…expands to:
3 3
switchexp generates a regular
expression suitable for the built-in regexp to select the words listed in its
arguments:
define(`switchexp',``^'switchexp_($@)`$'')dnl define(`switchexp_',`$1`'ifelse(eval($#`>1'),0,, ``\|'switchexp_(shift($@))')')dnl
…so switchexp(``ab'',``cd'',``ef'')
yields ^ab\|cd\|ef$.
There is no attempt to escape special regular-expression characters in the words.
foreach reproduces its third
argument once for each element of its second argument. A
macro named by the first argument is defined to expand to
the element each time:
define(`foreach', `ifelse(dequote(keepn(1,$2)),,, `pushdef(`$1', substr(keepn(1, $2),0,, ifelse(`$4',,``1'',`eval(`$4+1')')))`'$3`'popdef(`$1')` 'foreach(`$1', `shift($2)',shiftn(2,$@))')')dnl
For example:
foreach(`XX',```a'',``b'',``c'',``d'',``e''',``Letter 'XX` '')dnl
…expands to:
Letter a Letter b Letter c Letter d Letter e
The fourth argument specifies how many pairs of quotes should be added after expanding the iterator macro (default 0). This is useful if you want to create an argument list suitable for another macro. For example, you might expect that:
foreach(`XX', ```a'',``b'', ``c''', `,`toupper(`XX')'')
…yields:
,toupper(``a''),toupper(``b''),toupper(``c'')
…but instead it yields:
,toupper(`XX'),toupper(`XX'),toupper(`XX')
…because XX was never expanded. If
you unquote sufficiently, e.g.:
foreach(`XX', ```a'',``b'', ``c''', `,`toupper('XX`)'')
…you'll get:
,toupper(a),toupper(b),toupper(c)
This is fine, so long as a,
b and c,
are not macros.
By reinstating two pairs of quotes:
foreach(`XX', ```a'',``b'', ``c''', `,`toupper('XX`)'',`2')
…we get the intended result:
,toupper(``a''),toupper(``b''),toupper(``c'')
You may prefer to use the macro quoten.
Updated: 2007-May-11 16:56 GMT
Contact
Steven Simpson
Ĉi tiu paĝo disponeblas ĉi-lingve, laŭ via krozila agordo.