From 9f83c383e42d65c03c463b55d2ab4faba2bfdeff Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Mon, 16 Oct 2023 13:42:16 -0700 Subject: [PATCH] preproc, %map(): require second colon, update documentation Require the second colon before the grouped parameter count; otherwise the syntax is ambiguous since an expression can start with (. Update/complete the documentation and the examples. Signed-off-by: H. Peter Anvin --- asm/preproc.c | 58 ++++++++++++++++++++++++------------------------- doc/nasmdoc.src | 58 ++++++++++++++++++++++++++++++++++++++++--------- test/map.asm | 14 +++++++++--- 3 files changed, 88 insertions(+), 42 deletions(-) diff --git a/asm/preproc.c b/asm/preproc.c index e8070ea2..cf495bcd 100644 --- a/asm/preproc.c +++ b/asm/preproc.c @@ -7455,41 +7455,41 @@ stdmac_map(const SMacro *s, Token **params, int nparam) fixargs = NULL; fixparams = 0; + mparams = 1; t = skip_white(t->next); if (tok_is(t, ':')) { fixargs = t->next; fixparams = count_smacro_args(fixargs, &t); - if (fixparams) - t = skip_white(t->next); - } - mparams = 1; - if (tok_is(t, ':')) { - struct ppscan pps; - struct tokenval tokval; - expr *evalresult; - Token *ep; + t = skip_white(t->next); - pps.tptr = ep = zap_white(expand_smacro_noreset(t->next)); - t->next = NULL; - pps.ntokens = -1; - tokval.t_type = TOKEN_INVALID; - evalresult = evaluate(ppscan, &pps, &tokval, NULL, true, NULL); - free_tlist(ep); + if (tok_is(t, ':')) { + struct ppscan pps; + struct tokenval tokval; + expr *evalresult; + Token *ep; - if (!evalresult || tokval.t_type) { - nasm_nonfatal("invalid expression in parameter count for `%s' in function %s", - mname, s->name); - return NULL; - } else if (!is_simple(evalresult)) { - nasm_nonfatal("non-constant expression in parameter count for `%s' in function %s", - mname, s->name); - return NULL; - } - mparams = reloc_value(evalresult); - if (mparams < 1) { - nasm_nonfatal("invalid parameter count for `%s' in function %s", - mname, s->name); - return NULL; + pps.tptr = ep = zap_white(expand_smacro_noreset(t->next)); + t->next = NULL; + pps.ntokens = -1; + tokval.t_type = TOKEN_INVALID; + evalresult = evaluate(ppscan, &pps, &tokval, NULL, true, NULL); + free_tlist(ep); + + if (!evalresult || tokval.t_type) { + nasm_nonfatal("invalid expression in parameter count for `%s' in function %s", + mname, s->name); + return NULL; + } else if (!is_simple(evalresult)) { + nasm_nonfatal("non-constant expression in parameter count for `%s' in function %s", + mname, s->name); + return NULL; + } + mparams = reloc_value(evalresult); + if (mparams < 1) { + nasm_nonfatal("invalid parameter count for `%s' in function %s", + mname, s->name); + return NULL; + } } } diff --git a/doc/nasmdoc.src b/doc/nasmdoc.src index 42bee347..3aabd6c8 100644 --- a/doc/nasmdoc.src +++ b/doc/nasmdoc.src @@ -2901,7 +2901,7 @@ the macro. Note that just as for single-line macros, \c{%count()} treats an empty argument list as a single empty argument. \c %xdefine empty %count() ; %define empty 1 -\c %xdefine one %count(1) ; %define one 1 +\c %xdefine one %count(1) ; %define one 1 \c %xdefine two %count(5,q) ; %define two 2 \c %define list a,b,46 \c %xdefine lc1 %count(list) ; %define lc 1 (just one argument) @@ -2959,22 +2959,60 @@ argument to the conditional using \c{\{\}}: \S{f_map} \i\c{%map()} Function The \c{%map()} function takes as its first parameter the name of a -single-line macro, optionally followed by a colon and an integer -expression (default 1), specifying the number of parameter to the -macro, \e{n}. +single-line macro, followed by up to two optional colon-separated +subparameters: -The following parameters are then passed as parameters to the given -macro for expansion, in groups of \e{n}, and the results turned into a -comma-separated list. +\b The first subparameter, if present, should be a list of macro +parameters enclosed in parentheses. Note that \c{()} represents a +one-argument list containing an empty parameter; omit the parentheses +to specify no parameters. + +\b The second subparameter, if present, represent the number of +group size for additional parameters to the macro (default 1). + +Further parameters, if any, are then passed as additional parameters to the +given macro for expansion, in sets given by the specified group size, +and the results turned into a comma-separated list. If no additional +parameters are given, \c{%map()} expands to nothing. For example: -\c %define alpha(&x,y) y dup (x) -\c db %map(alpha:2,foo,bar,baz,quux) +\c %define alpha(&x) x +\c %define alpha(&x,y) y dup (x) +\c %define alpha(s,&x,y) y dup (x,s) +\c ; 0 fixed + 1 grouped parameters per call, calls alpha(&x) +\c db %map(alpha,foo,bar,baz,quux) +\c ; 0 fixed + 2 grouped parameters per call, calls alpha(&x,y) +\c db %map(alpha::2,foo,bar,baz,quux) +\c ; 1 fixed + 2 grouped parameters per call, calls alpha(s,&x,y) +\c db %map(alpha:("!"):2,foo,bar,baz,quux) ... expands to: -\c db bar dup ("foo"),quux dup ("baz") +\c db 'foo','bar','baz','quux' +\c db bar dup ('foo'),quux dup ('baz') +\c db bar dup ('foo',"!"),quux dup ('baz',"!") + +As a more complex example, a macro that joins quoted strings together +with a user-specified delimiter string: + +\c %define join(sep) '' ; handle the case of zero strings +\c %define _join(sep,str) sep,str ; helper macro +\c %define join(sep,s1,sn+) %strcat(s1, %map(_join:(sep) %, sn)) +\c +\c db join(':') +\c db join(':','a') +\c db join(':','a','b') +\c db join(':','a','b','c') +\c db join(':','a','b','c','d') + +... expands to: + +\c db '' +\c db 'a' +\c db 'a:b' +\c db 'a:b:c' +\c db 'a:b:c:d' \S{f_num} \i\c{%num()} Function diff --git a/test/map.asm b/test/map.asm index 48f4739d..2538b7d5 100644 --- a/test/map.asm +++ b/test/map.asm @@ -1,7 +1,15 @@ %define foo(x) (x+1) %define bar(=x,y) (x*y) %define baz(x+) %(x) - dw %map(foo,1,2,3,4) - dw %map(bar:2,1+2,3+4,5+6,7+8) - dw %map(baz:2,1+2,3+4,5+6,7+8) + dw %map(bar::2,1+2,3+4,5+6,7+8) + dw %map(baz::2,1+2,3+4,5+6,7+8) + +bar equ 8 +quux equ 4 +%define alpha(&x) x +%define alpha(&x,y) y dup (x) +%define alpha(s,&x,y) y dup (x,s) + db %map(alpha,foo,bar,baz,quux) + db %map(alpha::2,foo,bar,baz,quux) + db %map(alpha:("!"):2,foo,bar,baz,quux)