[nasm:master] preproc: add %abs(), modify %num(), refactor
nasm-bot for H. Peter Anvin
hpa at zytor.com
Thu Nov 17 15:33:07 PST 2022
Commit-ID: 36cd305ade2cf16d891e6f78404cf77196d60a02
Gitweb: http://repo.or.cz/w/nasm.git?a=commitdiff;h=36cd305ade2cf16d891e6f78404cf77196d60a02
Author: H. Peter Anvin <hpa at zytor.com>
AuthorDate: Thu, 17 Nov 2022 15:29:10 -0800
Committer: H. Peter Anvin <hpa at zytor.com>
CommitDate: Thu, 17 Nov 2022 15:29:10 -0800
preproc: add %abs(), modify %num(), refactor
Add the %abs() function, to produce the absolute value as an
always-positive decimal constant.
Change the order of the arguments for %num().
Refactor the handling of optional arguments, to reduce the amount of
redundant code. This is currently only used for builtin functions, but
might be extended in the future.
Signed-off-by: H. Peter Anvin <hpa at zytor.com>
---
asm/preproc.c | 208 +++++++++++++++++++++++++++++++++++-------------------
doc/nasmdoc.src | 17 ++++-
nasmlib/readnum.c | 11 +--
test/numtest.asm | 47 ++++++++++++
4 files changed, 202 insertions(+), 81 deletions(-)
diff --git a/asm/preproc.c b/asm/preproc.c
index 8f3a2cb5..c59ba4fb 100644
--- a/asm/preproc.c
+++ b/asm/preproc.c
@@ -201,20 +201,24 @@ typedef Token *(*ExpandSMacro)(const SMacro *s, Token **params, int nparams);
/*
* Store the definition of a single-line macro.
*
- * Note: SPARM_VARADIC is only used by internal "magic" macros.
+ * Note: for user-defined macros, SPARM_VARADIC and SPARM_DEFAULT are
+ * currently never set, and SPARM_OPTIONAL is set if and only
+ * if SPARM_GREEDY is set.
*/
enum sparmflags {
- SPARM_PLAIN = 0,
- SPARM_EVAL = 1, /* Evaluate as a numeric expression (=) */
- SPARM_STR = 2, /* Convert to quoted string ($) */
- SPARM_NOSTRIP = 4, /* Don't strip braces (!) */
- SPARM_GREEDY = 8, /* Greedy final parameter (+) */
- SPARM_VARADIC = 16 /* Zero or more individual arguments (...) */
+ SPARM_PLAIN = 0,
+ SPARM_EVAL = 1, /* Evaluate as a numeric expression (=) */
+ SPARM_STR = 2, /* Convert to quoted string ($) */
+ SPARM_NOSTRIP = 4, /* Don't strip braces (!) */
+ SPARM_GREEDY = 8, /* Greedy final parameter (+) */
+ SPARM_VARADIC = 16, /* Any number of separate arguments */
+ SPARM_OPTIONAL = 32 /* Optional argument */
};
struct smac_param {
Token name;
enum sparmflags flags;
+ const Token *def; /* Default, if any */
};
struct SMacro {
@@ -224,7 +228,8 @@ struct SMacro {
ExpandSMacro expand;
intorptr expandpvt;
struct smac_param *params;
- int nparam;
+ int nparam; /* length of the params structure */
+ int nparam_min; /* allows < nparam arguments */
int in_progress;
bool recursive;
bool varadic; /* greedy or supports > nparam arguments */
@@ -978,6 +983,8 @@ static void free_smacro_members(SMacro *s)
for (i = 0; i < s->nparam; i++) {
if (s->params[i].name.len > INLINE_TEXT)
nasm_free(s->params[i].name.text.p.ptr);
+ if (s->params[i].def)
+ free_tlist((Token *)s->params[i].def);
}
nasm_free(s->params);
}
@@ -2404,8 +2411,9 @@ restart:
while (m) {
if (!mstrcmp(m->name, name, m->casesense && nocase) &&
- (nparam <= 0 || m->nparam == 0 || nparam == m->nparam ||
- (m->varadic && nparam >= m->nparam-1))) {
+ (nparam <= 0 || m->nparam == 0 ||
+ (nparam >= m->nparam_min &&
+ (m->varadic || nparam <= m->nparam)))) {
if (m->alias && !find_alias) {
if (!ppconf.noaliases) {
name = tok_text(m->expansion);
@@ -3015,7 +3023,7 @@ static int parse_smacro_template(Token ***tpp, SMacro *tmpl)
flags |= SPARM_NOSTRIP;
break;
case '+':
- flags |= SPARM_GREEDY;
+ flags |= SPARM_GREEDY|SPARM_OPTIONAL;
greedy = true;
break;
case ',':
@@ -3058,9 +3066,9 @@ finish:
}
*tpp = tn;
if (tmpl) {
- tmpl->nparam = nparam;
- tmpl->varadic = greedy;
- tmpl->params = params;
+ tmpl->nparam = nparam;
+ tmpl->varadic = greedy;
+ tmpl->params = params;
}
return nparam;
}
@@ -3185,11 +3193,12 @@ static SMacro *define_smacro(const char *mname, bool casesense,
*smhead = smac;
}
- smac->name = nasm_strdup(mname);
- smac->casesense = casesense;
- smac->expansion = reverse_tokens(expansion);
- smac->expand = smacro_expand_default;
- smac->nparam = nparam;
+ smac->name = nasm_strdup(mname);
+ smac->casesense = casesense;
+ smac->expansion = reverse_tokens(expansion);
+ smac->expand = smacro_expand_default;
+ smac->nparam = nparam;
+ smac->nparam_min = nparam;
if (tmpl) {
smac->params = tmpl->params;
smac->alias = tmpl->alias;
@@ -3198,9 +3207,21 @@ static SMacro *define_smacro(const char *mname, bool casesense,
smac->expand = tmpl->expand;
smac->expandpvt = tmpl->expandpvt;
}
- if (nparam && (tmpl->params[nparam-1].flags &
- (SPARM_GREEDY|SPARM_VARADIC)))
- smac->varadic = true;
+ if (nparam) {
+ int nparam_min = nparam;
+
+ smac->varadic =
+ !!(tmpl->params[nparam-1].flags &
+ (SPARM_GREEDY|SPARM_VARADIC));
+
+ while (nparam_min > 1) {
+ if (!(tmpl->params[nparam_min-1].flags & SPARM_OPTIONAL))
+ break;
+ nparam_min--;
+ }
+
+ smac->nparam_min = nparam_min;
+ }
}
if (ppdbg & (PDBG_SMACROS|PDBG_LIST_SMACROS)) {
list_smacro_def((smac->alias ? PP_DEFALIAS : PP_DEFINE)
@@ -5690,10 +5711,9 @@ static SMacro *expand_one_smacro(Token ***tpp)
}
if (!mstrcmp(m->name, mname, m->casesense)) {
- if (nparam == m->nparam)
+ if (nparam >= m->nparam_min &&
+ (m->varadic || nparam <= m->nparam))
break; /* It's good */
- if (m->varadic && nparam >= m->nparam-1)
- break; /* Also good */
}
m = m->next;
}
@@ -5717,13 +5737,19 @@ static SMacro *expand_one_smacro(Token ***tpp)
bool bracketed = false;
bool bad_bracket = false;
enum sparmflags flags;
+ const struct smac_param *mparm;
- if (m->params[m->nparam-1].flags & SPARM_GREEDY)
- nparam = m->nparam;
+ if (nparam > m->nparam) {
+ if (m->params[m->nparam-1].flags & SPARM_GREEDY)
+ nparam = m->nparam;
+ } else if (nparam < m->nparam) {
+ nparam = m->nparam; /* Missing optional arguments = empty */
+ }
paren = 1;
nasm_newn(params, nparam);
i = 0;
- flags = m->params[i].flags;
+ mparm = m->params;
+ flags = mparm->flags;
phead = pep = ¶ms[i];
*pep = NULL;
@@ -5755,8 +5781,10 @@ static SMacro *expand_one_smacro(Token ***tpp)
*pep = NULL;
bracketed = false;
skip = true;
- if (!(flags & SPARM_VARADIC))
- flags = m->params[i].flags;
+ if (!(flags & SPARM_VARADIC)) {
+ mparm++;
+ flags = mparm->flags;
+ }
}
break;
@@ -5812,11 +5840,15 @@ static SMacro *expand_one_smacro(Token ***tpp)
/*
* Possible further processing of parameters. Note that the
* ordering matters here.
+ *
+ * mparm points to the current parameter specification
+ * structure (struct smac_param); this may not match the index
+ * i in the case of varadic parameters.
*/
- flags = 0;
- for (i = 0; i < nparam; i++) {
- if (!(flags & SPARM_VARADIC))
- flags = m->params[i].flags;
+ for (i = 0, mparm = m->params;
+ i < nparam;
+ i++, mparm += !(flags & SPARM_VARADIC)) {
+ const enum sparmflags flags = mparm->flags;
if (flags & SPARM_EVAL) {
/* Evaluate this parameter as a number */
@@ -5828,8 +5860,16 @@ static SMacro *expand_one_smacro(Token ***tpp)
eval_param = zap_white(expand_smacro_noreset(params[i]));
params[i] = NULL;
- if ((flags & (SPARM_GREEDY|SPARM_VARADIC)) && !eval_param)
- continue;
+ if (!eval_param) {
+ /* empty argument */
+ if (mparm->def) {
+ params[i] = dup_tlist(mparm->def, NULL);
+ continue;
+ } else if (flags & SPARM_OPTIONAL) {
+ continue;
+ }
+ /* otherwise, allow evaluate() to generate an error */
+ }
pps.tptr = eval_param;
pps.ntokens = -1;
@@ -7090,19 +7130,11 @@ static Token *
stdmac_cond_sel(const SMacro *s, Token **params, int nparams)
{
int64_t which;
- bool err;
/*
* params[0] will have been generated by make_tok_num.
*/
- which = get_tok_num(params[0], &err);
- if (err) {
- /*
- * Not a valid number; an error message will already have
- * been generated by expand_one_smacro().
- */
- return NULL;
- }
+ which = get_tok_num(params[0], NULL);
if (s->expandpvt.u) {
/* Booleanize (for %cond): true -> 1, false -> 2 (else) */
@@ -7133,7 +7165,7 @@ stdmac_cond_sel(const SMacro *s, Token **params, int nparams)
return new_Token(NULL, tok_smac_param(which), "", 0);
}
-/* %count() */
+/* %count() function */
static Token *
stdmac_count(const SMacro *s, Token **params, int nparams)
{
@@ -7143,9 +7175,9 @@ stdmac_count(const SMacro *s, Token **params, int nparams)
return make_tok_num(NULL, nparams);
}
-/* %num() */
+/* %num() function */
static Token *
-stdmac_num(const SMacro *s, Token **params, int nparams)
+stdmac_num(const SMacro *s, Token **params, int nparam)
{
static const char num_digits[] =
"0123456789"
@@ -7155,7 +7187,8 @@ stdmac_num(const SMacro *s, Token **params, int nparams)
int64_t parm[3];
uint64_t n;
int64_t dparm, bparm;
- int i, nd;
+ unsigned int i;
+ int nd;
unsigned int base;
char numstr[256];
char * const endstr = numstr + sizeof numstr - 1;
@@ -7164,24 +7197,14 @@ stdmac_num(const SMacro *s, Token **params, int nparams)
char *p;
bool moredigits;
- if (nparams < 1 || nparams > (int)ARRAY_SIZE(parm)) {
- nasm_nonfatal("invalid number of parameters to %s()", s->name);
- return NULL;
- }
+ (void)nparam;
- parm[1] = 10; /* Default base */
- parm[2] = -1; /* Default digits */
-
- for (i = 0; i < nparams; i++) {
- bool err;
- parm[i] = get_tok_num(params[i], &err);
- if (err)
- return NULL;
- }
+ for (i = 0; i < (int)ARRAY_SIZE(parm); i++)
+ parm[i] = get_tok_num(params[i], NULL);
n = parm[0];
- bparm = parm[1];
- dparm = parm[2];
+ dparm = parm[1];
+ bparm = parm[2];
if (bparm < 2 || bparm > maxbase) {
nasm_nonfatal("invalid base %"PRId64" given to %s()",
@@ -7190,7 +7213,7 @@ stdmac_num(const SMacro *s, Token **params, int nparams)
}
base = bparm;
-
+
if (dparm < -maxlen || dparm > maxlen) {
nasm_nonfatal("digit count %"PRId64" specified to %s() too large",
dparm, s->name);
@@ -7217,6 +7240,29 @@ stdmac_num(const SMacro *s, Token **params, int nparams)
return new_Token(NULL, TOKEN_STR, p, endstr - p);
}
+/* %abs() function */
+static Token *
+stdmac_abs(const SMacro *s, Token **params, int nparam)
+{
+ char numbuf[24];
+ int len;
+ int64_t v;
+ uint64_t u;
+
+ (void)s;
+ (void)nparam;
+
+ v = get_tok_num(params[0], NULL);
+ u = v < 0 ? -v : v;
+
+ /*
+ * Don't use make_tok_num() here, to make sure we don't emit
+ * a minus sign for the case of v = -2^63
+ */
+ len = snprintf(numbuf, sizeof numbuf, "%"PRIu64, u);
+ return new_Token(NULL, TOKEN_NUM, numbuf, len);
+}
+
/* Add magic standard macros */
struct magic_macros {
const char *name;
@@ -7239,9 +7285,9 @@ static void pp_add_magic_stdmac(void)
{ "__?LINE?__", true, 0, 0, stdmac_line },
{ "__?BITS?__", true, 0, 0, stdmac_bits },
{ "__?PTR?__", true, 0, 0, stdmac_ptr },
+ { "%abs", false, 1, SPARM_EVAL, stdmac_abs },
{ "%count", false, 1, SPARM_VARADIC, stdmac_count },
{ "%eval", false, 1, SPARM_EVAL|SPARM_VARADIC, stdmac_join },
- { "%num", false, 1, SPARM_EVAL|SPARM_VARADIC, stdmac_num },
{ "%str", false, 1, SPARM_GREEDY|SPARM_STR, stdmac_join },
{ "%strcat", false, 1, SPARM_GREEDY, stdmac_strcat },
{ "%strlen", false, 1, 0, stdmac_strlen },
@@ -7271,7 +7317,8 @@ static void pp_add_magic_stdmac(void)
nasm_newn(tmpl.params, m->nparam);
for (i = m->nparam-1; i >= 0; i--) {
tmpl.params[i].flags = flags;
- flags &= ~(SPARM_GREEDY|SPARM_VARADIC); /* Last arg only */
+ /* These flags for the last arg only */
+ flags &= ~(SPARM_GREEDY|SPARM_VARADIC|SPARM_OPTIONAL);
}
}
define_smacro(m->name, m->casesense, NULL, &tmpl);
@@ -7283,21 +7330,36 @@ static void pp_add_magic_stdmac(void)
}
/* %sel() function */
+ nasm_zero(tmpl);
tmpl.nparam = 2;
tmpl.recursive = true;
tmpl.expand = stdmac_cond_sel;
- nasm_newn(tmpl.params, 2);
+ nasm_newn(tmpl.params, tmpl.nparam);
tmpl.params[0].flags = SPARM_EVAL;
tmpl.params[1].flags = SPARM_VARADIC;
define_smacro("%sel", false, NULL, &tmpl);
/* %cond() function, a variation on %sel */
+ tmpl.nparam = 3;
tmpl.expandpvt.u = 1; /* Booleanize */
- for (tmpl.nparam = 2; tmpl.nparam <= 3; tmpl.nparam++) {
- nasm_newn(tmpl.params, tmpl.nparam);
- tmpl.params[0].flags = SPARM_EVAL;
- define_smacro("%cond", false, NULL, &tmpl);
- }
+ nasm_newn(tmpl.params, tmpl.nparam);
+ tmpl.params[0].flags = SPARM_EVAL;
+ tmpl.params[1].flags = 0;
+ tmpl.params[2].flags = SPARM_OPTIONAL;
+ define_smacro("%cond", false, NULL, &tmpl);
+
+ /* %num() function */
+ nasm_zero(tmpl);
+ tmpl.nparam = 3;
+ tmpl.expand = stdmac_num;
+ tmpl.recursive = true;
+ nasm_newn(tmpl.params, tmpl.nparam);
+ tmpl.params[0].flags = SPARM_EVAL;
+ tmpl.params[1].flags = SPARM_EVAL|SPARM_OPTIONAL;
+ tmpl.params[1].def = make_tok_num(NULL, -1);
+ tmpl.params[2].flags = SPARM_EVAL|SPARM_OPTIONAL;
+ tmpl.params[2].def = make_tok_num(NULL, 10);
+ define_smacro("%num", false, NULL, &tmpl);
/* %is...() macro functions */
nasm_zero(tmpl);
diff --git a/doc/nasmdoc.src b/doc/nasmdoc.src
index 39c38480..0883be4b 100644
--- a/doc/nasmdoc.src
+++ b/doc/nasmdoc.src
@@ -2860,6 +2860,13 @@ arguments, and can be used in any context where single-line macro
expansion would be performed. Preprocessor functions were introduced
in NASM 2.16.
+\S{f_abs} \i\c{%abs()} Function
+
+The \c{%abs()} function evaluates its first argument as an expression,
+and then emits the absolute value. This will always be emitted as a
+single token containing a decimal number; no minus sign will be
+emitted even if the input value is the maximum negative number.
+
\S{f_cond} \i\c{%cond()} Function
The \c{%cond()} function evaluates its first argument as an
@@ -2934,9 +2941,9 @@ argument to the conditional using \c{\{\}}:
The \c{%num()} function evaluates its arguments as expressions, and
then produces a quoted string encoding the first argument as an
-\e{unsigned} integer. The second argument is the encoding base (from 2
-to 64, default 10) and the third argument is the desired number of
-digits (max 253, default -1).
+\e{unsigned} integer. The second argument is the desired number of
+digits (max 253, default -1), and the second argument is the encoding
+base (from 2 to 64, default 10.)
Only the first argument is required.
@@ -2949,6 +2956,10 @@ The full 64-symbol set used is, in order:
\c 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ at _
+If a \e{signed} number needs to be converted to a string, use
+\c{%abs()}, \c{%cond()}, and \c{%strcat()} to format the signed number
+string to your specific output requirements.
+
\S{f_sel} \i\c{%sel()} Function
diff --git a/nasmlib/readnum.c b/nasmlib/readnum.c
index f2ec20cf..947bda53 100644
--- a/nasmlib/readnum.c
+++ b/nasmlib/readnum.c
@@ -75,7 +75,8 @@ int64_t readnum(const char *str, bool *error)
bool warn = false;
int sign = 1;
- *error = false;
+ if (error)
+ *error = true;
while (nasm_isspace(*r))
r++; /* find start of number */
@@ -97,8 +98,7 @@ int64_t readnum(const char *str, bool *error)
len = q-r;
if (!len) {
/* Not numeric */
- *error = true;
- return 0;
+ return 0;
}
/*
@@ -150,8 +150,7 @@ int64_t readnum(const char *str, bool *error)
if (*r != '_') {
if (*r < '0' || (*r > '9' && *r < 'A')
|| (digit = numvalue(*r)) >= radix) {
- *error = true;
- return 0;
+ return 0;
}
if (result > checklimit ||
(result == checklimit && digit >= last)) {
@@ -174,5 +173,7 @@ int64_t readnum(const char *str, bool *error)
str);
}
+ if (error)
+ *error = false;
return result * sign;
}
diff --git a/test/numtest.asm b/test/numtest.asm
new file mode 100644
index 00000000..bfe0237e
--- /dev/null
+++ b/test/numtest.asm
@@ -0,0 +1,47 @@
+%define a 64
+%define b -30
+
+ dq a*b
+ db %num(a*b,,16), `\n`
+ db %num(a*b,16,16), `\n`
+
+ db %num(a*b), `\n`
+ db %num(a*b,10), `\n`
+ db %num(a*b,3), `\n`
+ db %num(a*b,-3), `\n`
+ db %num(a*b,10,2), `\n`
+ db %num(a*b,-10,2), `\n`
+ db %num(a*b,,2), `\n`
+
+ dq -a*b
+ db %num(-a*b,,16), `\n`
+ db %num(-a*b,16,16), `\n`
+
+ db %num(-a*b), `\n`
+ db %num(-a*b,10), `\n`
+ db %num(-a*b,3), `\n`
+ db %num(-a*b,-3), `\n`
+ db %num(-a*b,10,2), `\n`
+ db %num(-a*b,-10,2), `\n`
+
+ dq %abs(a*b)
+ db %num(%abs(a*b),,16), `\n`
+ db %num(%abs(a*b),16,16), `\n`
+
+ db %num(%abs(a*b)), `\n`
+ db %num(%abs(a*b),10), `\n`
+ db %num(%abs(a*b),3), `\n`
+ db %num(%abs(a*b),-3), `\n`
+ db %num(%abs(a*b),10,2), `\n`
+ db %num(%abs(a*b),-10,2), `\n`
+
+ dq %abs(-a*b)
+ db %num(%abs(-a*b),,16), `\n`
+ db %num(%abs(-a*b),16,16), `\n`
+
+ db %num(%abs(-a*b)), `\n`
+ db %num(%abs(-a*b),10), `\n`
+ db %num(%abs(-a*b),3), `\n`
+ db %num(%abs(-a*b),-3), `\n`
+ db %num(%abs(-a*b),10,2), `\n`
+ db %num(%abs(-a*b),-10,2), `\n`
More information about the Nasm-commits
mailing list