[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(&params[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(&params[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