[nasm:master] preproc: implement the %cond() and %sel() functions; fix memory leak
nasm-bot for H. Peter Anvin
hpa at zytor.com
Sun Nov 13 23:03:05 PST 2022
Commit-ID: d9b36e3c9c3400c84b9d455c231e4e7b12bc19a1
Gitweb: http://repo.or.cz/w/nasm.git?a=commitdiff;h=d9b36e3c9c3400c84b9d455c231e4e7b12bc19a1
Author: H. Peter Anvin <hpa at zytor.com>
AuthorDate: Sun, 13 Nov 2022 22:59:16 -0800
Committer: H. Peter Anvin <hpa at zytor.com>
CommitDate: Sun, 13 Nov 2022 22:59:16 -0800
preproc: implement the %cond() and %sel() functions; fix memory leak
Implement the %cond() and %sel() functions that expand to a specific
one of the arguments. %cond(x,y,z) is basically a shorthand for
%sel(2-!(x),y,z) used when x is a boolean condition.
Fix a memory leak in %strcat and %strlen.
Signed-off-by: H. Peter Anvin <hpa at zytor.com>
---
asm/preproc.c | 126 ++++++++++++++++++++++++++++++++++++++++++++++----------
doc/nasmdoc.src | 26 ++++++++++++
2 files changed, 130 insertions(+), 22 deletions(-)
diff --git a/asm/preproc.c b/asm/preproc.c
index 4474ca68..fdb761b7 100644
--- a/asm/preproc.c
+++ b/asm/preproc.c
@@ -644,6 +644,7 @@ static Token *expand_smacro(Token * tline);
static Token *expand_id(Token * tline);
static Context *get_ctx(const char *name, const char **namep);
static Token *make_tok_num(Token *next, int64_t val);
+static int64_t get_tok_num(const Token *t, bool *err);
static Token *make_tok_qstr(Token *next, const char *str);
static Token *make_tok_qstr_len(Token *next, const char *str, size_t len);
static Token *make_tok_char(Token *next, char op);
@@ -3584,6 +3585,7 @@ static Token *pp_strcat(Token *tline, const char *dname)
size_t len;
Token *t;
+ Token *res = NULL;
char *q, *qbuf;
len = 0;
@@ -3601,8 +3603,7 @@ static Token *pp_strcat(Token *tline, const char *dname)
default:
nasm_nonfatal("non-string passed to `%s': %s", dname,
tok_text(t));
- free_tlist(tline);
- return NULL;
+ goto err;
}
}
@@ -3613,9 +3614,11 @@ static Token *pp_strcat(Token *tline, const char *dname)
}
*q = '\0';
- return make_tok_qstr_len(NULL, qbuf, len);
+ res = make_tok_qstr_len(NULL, qbuf, len);
nasm_free(qbuf);
- return t;
+err:
+ free_tlist(tline);
+ return res;
}
/*
@@ -3629,6 +3632,7 @@ static Token *pp_substr(Token *tline, const char *dname)
size_t len;
struct ppscan pps;
Token *t;
+ Token *res = NULL;
expr *evalresult;
struct tokenval tokval;
@@ -3636,7 +3640,7 @@ static Token *pp_substr(Token *tline, const char *dname)
if (!tok_is(t, TOKEN_STR)) {
nasm_nonfatal("`%s' requires a string as parameter", dname);
- return NULL;
+ goto err;
}
pps.tptr = skip_white(t->next);
@@ -3644,17 +3648,17 @@ static Token *pp_substr(Token *tline, const char *dname)
pps.tptr = skip_white(pps.tptr->next);
if (!pps.tptr) {
nasm_nonfatal("`%s' requires a starting index", dname);
- return NULL;
+ goto err;
}
pps.ntokens = -1;
tokval.t_type = TOKEN_INVALID;
evalresult = evaluate(ppscan, &pps, &tokval, NULL, true, NULL);
if (!evalresult) {
- return NULL;
+ goto err;
} else if (!is_simple(evalresult)) {
nasm_nonfatal("non-constant value given to `%s'", dname);
- return NULL;
+ goto err;
}
start = evalresult->value - 1;
@@ -3665,10 +3669,10 @@ static Token *pp_substr(Token *tline, const char *dname)
tokval.t_type = TOKEN_INVALID;
evalresult = evaluate(ppscan, &pps, &tokval, NULL, true, NULL);
if (!evalresult) {
- return NULL;
+ goto err;
} else if (!is_simple(evalresult)) {
nasm_nonfatal("non-constant value given to `%s'", dname);
- return NULL;
+ goto err;
}
count = evalresult->value;
}
@@ -3687,7 +3691,10 @@ static Token *pp_substr(Token *tline, const char *dname)
start = -1, count = 0; /* empty string */
txt = (start < 0) ? "" : tok_text(t) + start;
- return make_tok_qstr_len(NULL, txt, count);
+ res = make_tok_qstr_len(NULL, txt, count);
+err:
+ free_tlist(tline);
+ return res;
}
/**
@@ -4850,7 +4857,6 @@ issue_error:
*/
if (macro_start)
define_smacro(mname, casesense, macro_start, NULL);
- free_tlist(tline);
break;
case PP_SUBSTR:
@@ -4869,7 +4875,6 @@ issue_error:
*/
if (macro_start)
define_smacro(mname, casesense, macro_start, NULL);
- free_tlist(tline);
break;
case PP_ASSIGN:
@@ -6909,22 +6914,34 @@ stdmac_join(const SMacro *s, Token **params, int nparams)
static Token *
stdmac_strcat(const SMacro *s, Token **params, int nparams)
{
- nasm_assert(nparams == 1);
- return pp_strcat(expand_smacro_noreset(params[0]), s->name);
+ Token *tline;
+ (void)nparams;
+
+ tline = params[0];
+ params[0] = NULL; /* Don't free this later */
+ return pp_strcat(expand_smacro_noreset(tline), s->name);
}
/* %substr() function */
static Token *
stdmac_substr(const SMacro *s, Token **params, int nparams)
{
- nasm_assert(nparams == 1);
- return pp_substr(expand_smacro_noreset(params[0]), s->name);
+ Token *tline;
+ (void)nparams;
+
+ tline = params[0];
+ params[0] = NULL; /* Don't free this later */
+ return pp_substr(expand_smacro_noreset(tline), s->name);
}
/* Expand a the argument and enforce it being a single quoted string */
-static Token *expand_to_string(Token *tlist, const char *dname)
+static Token *expand_to_string(Token **tp, const char *dname)
{
- Token *t = zap_white(expand_smacro_noreset(tlist));
+ Token *tlist, *t;
+
+ tlist = *tp;
+ *tp = NULL; /* Don't free this later */
+ t = zap_white(expand_smacro_noreset(tlist));
if (!tok_is(t, TOKEN_STR)) {
nasm_nonfatal("`%s' requires string as parameter", dname);
@@ -6948,7 +6965,7 @@ stdmac_strlen(const SMacro *s, Token **params, int nparams)
(void)nparams;
- t = expand_to_string(params[0], s->name);
+ t = expand_to_string(¶ms[0], s->name);
if (!t)
return NULL;
@@ -6964,13 +6981,38 @@ stdmac_tok(const SMacro *s, Token **params, int nparams)
(void)nparams;
- t = expand_to_string(params[0], s->name);
+ t = expand_to_string(¶ms[0], s->name);
if (!t)
return NULL;
return reverse_tokens(tokenize(unquote_token_cstr(t)));
}
+/* %cond() or %sel() */
+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)
+ return NULL; /* Nothing to expand */
+
+ if (s->expandpvt.u) {
+ /* Booleanize (for %cond): true -> 1, false -> 2 (else) */
+ which = which ? 1 : 2;
+ }
+
+ if (which < 1 || which >= nparams)
+ return NULL;
+
+ return new_Token(NULL, tok_smac_param(which), "", 0);
+}
+
/* Add magic standard macros */
struct magic_macros {
const char *name;
@@ -7027,9 +7069,26 @@ static void pp_add_magic_stdmac(void)
}
}
+ /* %sel() function */
+ tmpl.nparam = 2;
+ tmpl.recursive = true;
+ tmpl.expand = stdmac_cond_sel;
+ nasm_newn(tmpl.params, 2);
+ 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.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);
+ }
+
/* %is...() macro functions */
+ nasm_zero(tmpl);
tmpl.nparam = 1;
- tmpl.varadic = true;
tmpl.expand = stdmac_is;
tmpl.recursive = true;
name_buf[0] = '%';
@@ -7644,6 +7703,29 @@ static Token *make_tok_num(Token *next, int64_t val)
return next;
}
+/*
+ * Do the inverse of make_tok_num(). This only needs to be able
+ * to parse the output of make_tok_num().
+ */
+static int64_t get_tok_num(const Token *t, bool *err)
+{
+ bool minus = false;
+ int64_t v;
+
+ if (tok_is(t, '-')) {
+ minus = true;
+ t = t->next;
+ }
+ if (!tok_is(t, TOKEN_NUM)) {
+ if (err)
+ *err = true;
+ return 0;
+ }
+
+ v = readnum(tok_text(t), err);
+ return minus ? -v : v;
+}
+
/* Create a quoted string token */
static Token *make_tok_qstr_len(Token *next, const char *str, size_t len)
{
diff --git a/doc/nasmdoc.src b/doc/nasmdoc.src
index 787b3eab..17eca992 100644
--- a/doc/nasmdoc.src
+++ b/doc/nasmdoc.src
@@ -2853,6 +2853,20 @@ 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_cond} \i\c{%cond()} Function
+
+The \c{%cond()} function evaluates its first argument as an
+expression, then expands to its second argument if true (nonzero), and
+the third, if present, if false (zero). This is in effect a specialized
+version of the \i\c{%sel()} function; \c{%cond(x,y,z)} is equivalent
+to \c{%sel(2-!(x),y,z)}.
+
+\c %define a 1
+\c %xdefine astr %cond(a,"true","false") ; %define astr "true"
+
+The argument not selected is never expanded.
+
+
\S{f_eval} \i\c{%eval()} Function
The \c{%eval()} function evaluates its argument as a numeric
@@ -2894,6 +2908,18 @@ argument to the conditional using \c{\{\}}:
\c %endif
+\S{f_sel} \i\c{%sel()} Function
+
+The \c{%sel()} function evaluates its first argument as an
+expression, then expands to its second argument if 1, the third
+argument if 2, and so on. If the value is less than 1 or larger than
+the number of arguments minus one, then the \c{%sel()} function
+expands to nothing.
+
+\c %define b 2
+\c %xdefine bstr %sel(b,"one","two","three") ; %define bstr "two"
+
+
\S{f_str} \i\c\{%str()} Function
The \c{%str()} function converts its argument, including any commas,
More information about the Nasm-commits
mailing list