[nasm:nasm-2.15.xx] preproc: add %*? and %*??

nasm-bot for H. Peter Anvin hpa at zytor.com
Sat Jul 18 13:54:10 PDT 2020


Commit-ID:  6263a2a4c2883f86a0339701bc0a99eaf2a783e6
Gitweb:     http://repo.or.cz/w/nasm.git?a=commitdiff;h=6263a2a4c2883f86a0339701bc0a99eaf2a783e6
Author:     H. Peter Anvin <hpa at zytor.com>
AuthorDate: Sat, 18 Jul 2020 13:47:59 -0700
Committer:  H. Peter Anvin <hpa at zytor.com>
CommitDate: Sat, 18 Jul 2020 13:47:59 -0700

preproc: add %*? and %*??

The %? and %?? tokens are ambiguous when used inside a multi-line
macro. Add tokens %*? and %*?? that only expand during single-macro
expansion.

Signed-off-by: H. Peter Anvin (Intel) <hpa at zytor.com>


---
 asm/preproc.c    | 161 ++++++++++++++++++++++++++++++++++++++++---------------
 doc/changes.src  |  12 +++++
 doc/nasmdoc.src  |  49 ++++++++++++++---
 test/selfref.asm |  24 +++++++++
 4 files changed, 196 insertions(+), 50 deletions(-)

diff --git a/asm/preproc.c b/asm/preproc.c
index fec9520c..b291437c 100644
--- a/asm/preproc.c
+++ b/asm/preproc.c
@@ -125,7 +125,8 @@ enum pp_token_type {
     TOK_LOCAL_MACRO, TOK_ENVIRON, TOK_STRING,
     TOK_NUMBER, TOK_FLOAT, TOK_OTHER,
     TOK_INTERNAL_STRING, TOK_NAKED_STRING,
-    TOK_PREPROC_Q, TOK_PREPROC_QQ,
+    TOK_PREPROC_Q, TOK_PREPROC_SQ, 	/* %?,  %*?  */
+    TOK_PREPROC_QQ, TOK_PREPROC_SQQ,    /* %??, %*?? */
     TOK_PASTE,              /* %+ */
     TOK_COND_COMMA,         /* %, */
     TOK_INDIRECT,           /* %[...] */
@@ -1456,6 +1457,11 @@ static Token *tokenize(const char *line)
                 p++;
                 if (*p == '?')
                     p++;
+            } else if (*p == '*' && p[1] == '?') {
+                /* %*? and %*?? */
+                p += 2;
+                if (*p == '?')
+                    p++;
             } else if (*p == '!') {
                 /* Environment variable reference */
                 p++;
@@ -1516,6 +1522,16 @@ static Token *tokenize(const char *line)
                         type = TOK_PREPROC_ID;
                     break;
 
+                case '*':
+                    type = TOK_OTHER;
+                    if (line[2] == '?') {
+                        if (toklen == 3)
+                            type = TOK_PREPROC_SQ;
+                        else if (toklen == 4 && line[3] == '?')
+                            type = TOK_PREPROC_SQQ;
+                    }
+                    break;
+
                 case '!':
                     type = (toklen == 2) ? TOK_OTHER : TOK_ENVIRON;
                     break;
@@ -1584,7 +1600,8 @@ static Token *tokenize(const char *line)
                 /* type = -1; */
             }
         } else if (p[0] == '$' && p[1] == '$') {
-            type = TOK_OTHER;   /* TOKEN_BASE */
+            /* TOKEN_BASE - treat as TOK_ID for pasting purposes */
+            type = TOK_ID;
             p += 2;
         } else if (nasm_isnumstart(*p)) {
             bool is_hex = false;
@@ -1650,7 +1667,8 @@ static Token *tokenize(const char *line)
             p--;        /* Point to first character beyond number */
 
             if (p == line+1 && *line == '$') {
-                type = TOK_OTHER; /* TOKEN_HERE */
+                /* TOKEN_HERE - treat as TOK_ID for pasting purposes */
+                type = TOK_ID;
             } else {
                 if (has_e && !is_hex) {
                     /* 1e13 is floating-point, but 1e13h is not */
@@ -2375,9 +2393,8 @@ restart:
                     continue;
                 }
             }
-            if (defn) {
-                *defn = (nparam == m->nparam || nparam == -1) ? m : NULL;
-            }
+            if (defn)
+                *defn = m;
             return true;
         }
         m = m->next;
@@ -3019,7 +3036,8 @@ static SMacro *define_smacro(const char *mname, bool casesense,
     struct hash_table *smtbl;
     Context *ctx;
     bool defining_alias = false;
-    unsigned int nparam = 0;
+    int nparam = 0;
+    bool defined;
 
     if (tmpl) {
         defining_alias = tmpl->alias;
@@ -3028,48 +3046,101 @@ static SMacro *define_smacro(const char *mname, bool casesense,
             mark_smac_params(expansion, tmpl, 0);
     }
 
-    while (1) {
-        ctx = get_ctx(mname, &mname);
-
-        if (!smacro_defined(ctx, mname, nparam, &smac, casesense, true)) {
-            /* Create a new macro */
-            smtbl  = ctx ? &ctx->localmac : &smacros;
-            smhead = (SMacro **) hash_findi_add(smtbl, mname);
-            nasm_new(smac);
-            smac->next = *smhead;
-            *smhead = smac;
-            break;
-        } else if (!smac) {
-            nasm_warn(WARN_OTHER, "single-line macro `%s' defined both with and"
-                       " without parameters", mname);
+    ctx = get_ctx(mname, &mname);
+
+    defined = smacro_defined(ctx, mname, nparam, &smac, casesense, true);
+
+    if (defined) {
+        if (smac->alias) {
+            if (smac->in_progress) {
+                nasm_nonfatal("macro alias loop");
+                goto fail;
+            }
+
+            if (!defining_alias && !ppopt.noaliases) {
+                /* It is an alias macro; follow the alias link */
+                SMacro *s;
+
+                smac->in_progress = true;
+                s = define_smacro(tok_text(smac->expansion), casesense,
+                                  expansion, tmpl);
+                smac->in_progress = false;
+                return s;
+            }
+        }
+
+        if (casesense ^ smac->casesense) {
             /*
-             * Some instances of the old code considered this a failure,
-             * some others didn't.  What is the right thing to do here?
+             *!macro-def-case-single [on] single-line macro defined both case sensitive and insensitive
+             *!  warns when a single-line macro is defined both case
+             *!  sensitive and case insensitive.
+             *!  The new macro
+             *!  definition will override (shadow) the original one,
+             *!  although the original macro is not deleted, and will
+             *!  be re-exposed if the new macro is deleted with
+             *!  \c{%undef}, or, if the original macro is the case
+             *!  insensitive one, the macro call is done with a
+             *!  different case.
              */
-            goto fail;
-        } else if (!smac->alias || ppopt.noaliases || defining_alias) {
+            nasm_warn(WARN_MACRO_DEF_CASE_SINGLE, "case %ssensitive definition of macro `%s' will shadow %ssensitive macro `%s'",
+                      casesense ? "" : "in",
+                      mname,
+                      smac->casesense ? "" : "in",
+                      smac->name);
+            defined = false;
+        } else if ((!!nparam) ^ (!!smac->nparam)) {
             /*
-             * We're redefining, so we have to take over an
-             * existing SMacro structure. This means freeing
-             * what was already in it, but not the structure itself.
+             * Most recent versions of NASM considered this an error,
+             * so promote this warning to error by default.
+             *
+             *!macro-def-param-single [err] single-line macro defined with and without parameters
+             *!  warns if the same single-line macro is defined with and
+             *!  without parameters.
+             *!  The new macro
+             *!  definition will override (shadow) the original one,
+             *!  although the original macro is not deleted, and will
+             *!  be re-exposed if the new macro is deleted with
+             *!  \c{%undef}.
              */
-            clear_smacro(smac);
-            break;
-        } else if (smac->in_progress) {
-            nasm_nonfatal("macro alias loop");
-            goto fail;
-        } else {
-            /* It is an alias macro; follow the alias link */
-            SMacro *s;
-
-            smac->in_progress = true;
-            s = define_smacro(tok_text(smac->expansion), casesense,
-                              expansion, tmpl);
-            smac->in_progress = false;
-            return s;
+            nasm_warn(WARN_MACRO_DEF_PARAM_SINGLE,
+                      "macro `%s' defined both with and without parameters",
+                      mname);
+            defined = false;
+        } else if (smac->nparam < nparam) {
+            /*
+             *!macro-def-greedy-single [on] single-line macro
+             *!  definition shadows greedy macro warns when a
+             *!  single-line macro is defined which would match a
+             *!  previously existing greedy definition.  The new macro
+             *!  definition will override (shadow) the original one,
+             *!  although the original macro is not deleted, and will
+             *!  be re-exposed if the new macro is deleted with
+             *!  \c{%undef}, and will be invoked if called with a
+             *!  parameter count that does not match the new definition.
+             */
+            nasm_warn(WARN_MACRO_DEF_GREEDY_SINGLE,
+                      "defining macro `%s' shadows previous greedy definition",
+                      mname);
+            defined = false;
         }
     }
 
+    if (defined) {
+        /*
+         * We're redefinining, so we have to take over an
+         * existing SMacro structure. This means freeing
+         * what was already in it, but not the structure itself.
+         */
+        clear_smacro(smac);
+    } else {
+        /* Create a new macro */
+        smtbl  = ctx ? &ctx->localmac : &smacros;
+        smhead = (SMacro **) hash_findi_add(smtbl, mname);
+        nasm_new(smac);
+        smac->next = *smhead;
+        *smhead = smac;
+    }
+
     smac->name      = nasm_strdup(mname);
     smac->casesense = casesense;
     smac->expansion = expansion;
@@ -4685,8 +4756,8 @@ issue_error:
     }
 
 done:
-        free_tlist(origline);
-        return DIRECTIVE_FOUND;
+    free_tlist(origline);
+    return DIRECTIVE_FOUND;
 }
 
 /*
@@ -5523,11 +5594,13 @@ static SMacro *expand_one_smacro(Token ***tpp)
 
         switch (type) {
         case TOK_PREPROC_Q:
+        case TOK_PREPROC_SQ:
             delete_Token(t);
             t = dup_Token(tline, mstart);
             break;
 
         case TOK_PREPROC_QQ:
+        case TOK_PREPROC_SQQ:
         {
             size_t mlen = strlen(m->name);
 	    size_t len;
diff --git a/doc/changes.src b/doc/changes.src
index d1182271..e0215f1d 100644
--- a/doc/changes.src
+++ b/doc/changes.src
@@ -7,6 +7,18 @@
 The NASM 2 series supports x86-64, and is the production version of NASM
 since 2007.
 
+\S{cl-2.15.04} Version 2.15.04
+
+\b More sensible handling of the case where one single-line macro
+definition will shadow another. A warning will be issued, but the
+additional definition will be allowed. For the existing error case
+where both a parameterless and parametered macro are created, that
+warning is promoted to an error by default.
+
+\b Add special preprocessor tokens \c{%*?} and \c{%*??} that expand
+like \c{%?} and \c{%??} in single-line macros only. See
+\k{selfref%*?}.
+
 \S{cl-2.15.03} Version 2.15.03
 
 \b Add instructions from the Intel Instruction Set Extensions and
diff --git a/doc/nasmdoc.src b/doc/nasmdoc.src
index 51a6b766..a112589e 100644
--- a/doc/nasmdoc.src
+++ b/doc/nasmdoc.src
@@ -2403,7 +2403,9 @@ macros, but for case-insensitive macros, they can differ.
 
 For example:
 
-\c %idefine Foo mov %?,%??
+\c %imacro Foo 0
+\c         mov %?,%??
+\c %endmacro
 \c
 \c         foo
 \c         FOO
@@ -2413,14 +2415,49 @@ will expand to:
 \c         mov foo,Foo
 \c         mov FOO,Foo
 
-The sequence:
+These tokens can be used for single-line macros \e{if defined outside
+any multi-line macros.} See below.
+
+\S{selfref%*?} The Single-Line Macro Name: \i\c{%*?} and \i\c{%*??}
+
+If the tokens \c{%?} and \c{%??} are used inside a multi-line macro,
+they are expanded before any directives are processed. As a result,
+
+\c %imacro Foo 0
+\c       %idefine Bar _%?
+\c       mov BAR,bAr
+\c %endmacro
+\c
+\c       foo
+\c       mov eax,bar
+
+will expand to:
+
+\c       mov _foo,_foo
+\c       mov eax,_foo
+
+which may or may not be what you expected. The tokens \c{%*?} and
+\c{%*??} behave like \c{%?} and \c{%??} but are only expanded inside
+single-line macros. Thus:
+
+\c %imacro Foo 0
+\c       %idefine Bar _%*?
+\c       mov BAR,bAr
+\c %endmacro
+\c
+\c       foo
+\c       mov eax,bar
+
+will expand to:
 
-\c %idefine keyword $%?
+\c       mov _BAR,_bAr
+\c       mov eax,_bar
 
-can be used to make a keyword "disappear", for example in case a new
-instruction has been used as a label in older code.  For example:
+The \c{%*?} can be used to make a keyword "disappear", for example in
+case a new instruction has been used as a label in older code.  For
+example:
 
-\c %idefine pause $%?                  ; Hide the PAUSE instruction
+\c %idefine pause $%*?                 ; Hide the PAUSE instruction
 
 
 \S{undef} Undefining Single-Line Macros: \i\c{%undef}
diff --git a/test/selfref.asm b/test/selfref.asm
new file mode 100644
index 00000000..90ecef06
--- /dev/null
+++ b/test/selfref.asm
@@ -0,0 +1,24 @@
+	bits 32
+
+%idefine zoom $%?
+	mov ebx,Zoom
+%idefine boom $%?
+	mov ecx,Boom
+
+%imacro Foo1 0
+	%idefine Bar1 _%?
+	%idefine baz1 $%?
+	mov BAR1,baz1
+%endmacro
+
+	foo1
+	mov eax,bar1
+
+%imacro Foo2 0
+	%idefine Bar2 _%*?
+	%idefine baz2 $%*?
+	mov BAR2,baz2
+%endmacro
+
+	foo2
+	mov eax,bar2


More information about the Nasm-commits mailing list