New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

implement __VA_OPT__ #135

Open
mycoboco opened this Issue Jan 2, 2018 · 3 comments

Comments

Projects
None yet
1 participant
@mycoboco
Owner

mycoboco commented Jan 2, 2018

Cases to handle:

  • __VA_OPT__ in __VA_OPT__()
  • __VA_OPT__ in other places
  • __VA_OPT__ and ##
    • ## __VA_OPT__ (...)
    • __VA_OPT__ ## (...)
    • __VA_OPT__ (## ...)
    • __VA_OPT__ (... ##)
    • __VA_OPT__ (...) ##
  • __VA_OPT__ and #
    • # __VA_OPT__ (...)
    • __VA_OPT__ # (...)
    • __VA_OPT__ (# ...)
    • __VA_OPT__ (... #)
    • __VA_OPT__ (...) #
  • __VA_OPT__ and spaces
    • __VA_OPT__ (...)
    • leading and trailing spaces within __VA_OPT__(...)
    • __VA_OPT__(...) (trailing space)
  • redefinitions
    • space between __VA_OPT__ and (
    #define foo(...) __VA_OPT__(fred)
    #define foo(...) __VA_OPT__ (fred)
    • space within __VA_OPT__()
    #define foo(...) __VA_OPT__( a)
    #define foo(...) __VA_OPT__(a )
    
    #define bar(...) __VA_OPT__ (+a)
    #define bar(...) __VA_OPT__ (+ a)
    • consecutive __VA_OPT__s
    #define foo(...) __VA_OPT__ (a b)
    #define foo(...) __VA_OPT__(a) __VA_OPT__(b)
  • avoid token paste
    • __VA_OPT__(foo)bar
    • __VA_OPT__(foo)__VA_OPT__(bar)

@mycoboco mycoboco self-assigned this Jan 2, 2018

@mycoboco

This comment has been minimized.

Show comment
Hide comment
@mycoboco

mycoboco Jan 3, 2018

Owner

Has so many corner cases that it seems too early to have a solid implementation.

Owner

mycoboco commented Jan 3, 2018

Has so many corner cases that it seems too early to have a solid implementation.

@mycoboco mycoboco removed their assignment Jan 3, 2018

@mycoboco

This comment has been minimized.

Show comment
Hide comment
@mycoboco

mycoboco Jan 3, 2018

Owner

Test cases:

#define foo1(...) __VA_OPT__(__VA_OPT__)
#define foo2(...) start __VA_OPT__(__VA_OPT__()) end
#define foo3(...) start __VA_OPT__(start __VA_OPT__(foo) end) end

#define foo4(...) ## __VA_OPT__(test)
#define foo5(...) test ## __VA_OPT__(test)

#define foo6(...) __VA_OPT__ ## (test)
#define foo7(...) __VA_OPT__##(test)

#define foo8(...) __VA_OPT__(##test)
#define foo9(...) test __VA_OPT__(##) test
#define foo10(...) test __VA_OPT__( ## test) test

#define foo11(...) __VA_OPT__(test ##)
#define foo12(...) test __VA_OPT__(test## ) test

#define foo13(...) __VA_OPT__(test)##
#define foo14(...) __VA_OPT__(test) ##
#define foo15(...) test __VA_OPT__(test) ##

#define foo16(...) # __VA_OPT__()
#define foo17(...) #__VA_OPT__(test)

#define foo18(...) __VA_OPT__#()
#define foo19(a, ...) __VA_OPT__ # (a)

#define foo20(...) __VA_OPT__(#)
#define foo21(a, ...) __VA_OPT__(#) a
#define foo22(a, ...) __VA_OPT__(#a)

#define foo23(...) __VA_OPT__(test #)
#define foo24(...) __VA_OPT__(test # ) test
#define foo25(a, ...) __VA_OPT__(test # ) a

#define foo26(...) __VA_OPT__ (test)
#define foo27(...) __VA_OPT__ (    test    )
foo27(test)
foo27()
#define foo28(...) __VA_OPT__(test)fred
foo28()
foo28(test)
#define foo29(...) __VA_OPT__(test) fred
foo29()
foo29(test)
#define foo30(...) __VA_OPT__(test)__VA_OPT__(fred)
foo30()
foo30(test)

#define foo31(__VA_OPT__) #__VA_OPT__
foo31(foo)
#define foo32(__VA_OPT__, ...) #__VA_OPT__()
foo32(foo)

#define foo33(...) __VA_OPT__(fred)
#define foo33(...) __VA_OPT__ (fred)

#define foo34(...) __VA_OPT__ (a )
#define foo34(...) __VA_OPT__ ( a)

#define foo35(...) __VA_OPT__ (+a)
#define foo35(...) __VA_OPT__ (+ a)

#define foo36(...) __VA_OPT__ (a b)
#define foo36(...) __VA_OPT__(a) __VA_OPT__(b)

#define foo37(a, ...) start __VA_OPT__(,) end
foo37(foo)
foo37(foo,)
foo37(foo,bar)

/* https://gcc.gnu.org/bugzilla/show_bug.cgi?id=83063 */
#define ice(...) b##__VA_OPT__ ()
ice ()
Owner

mycoboco commented Jan 3, 2018

Test cases:

#define foo1(...) __VA_OPT__(__VA_OPT__)
#define foo2(...) start __VA_OPT__(__VA_OPT__()) end
#define foo3(...) start __VA_OPT__(start __VA_OPT__(foo) end) end

#define foo4(...) ## __VA_OPT__(test)
#define foo5(...) test ## __VA_OPT__(test)

#define foo6(...) __VA_OPT__ ## (test)
#define foo7(...) __VA_OPT__##(test)

#define foo8(...) __VA_OPT__(##test)
#define foo9(...) test __VA_OPT__(##) test
#define foo10(...) test __VA_OPT__( ## test) test

#define foo11(...) __VA_OPT__(test ##)
#define foo12(...) test __VA_OPT__(test## ) test

#define foo13(...) __VA_OPT__(test)##
#define foo14(...) __VA_OPT__(test) ##
#define foo15(...) test __VA_OPT__(test) ##

#define foo16(...) # __VA_OPT__()
#define foo17(...) #__VA_OPT__(test)

#define foo18(...) __VA_OPT__#()
#define foo19(a, ...) __VA_OPT__ # (a)

#define foo20(...) __VA_OPT__(#)
#define foo21(a, ...) __VA_OPT__(#) a
#define foo22(a, ...) __VA_OPT__(#a)

#define foo23(...) __VA_OPT__(test #)
#define foo24(...) __VA_OPT__(test # ) test
#define foo25(a, ...) __VA_OPT__(test # ) a

#define foo26(...) __VA_OPT__ (test)
#define foo27(...) __VA_OPT__ (    test    )
foo27(test)
foo27()
#define foo28(...) __VA_OPT__(test)fred
foo28()
foo28(test)
#define foo29(...) __VA_OPT__(test) fred
foo29()
foo29(test)
#define foo30(...) __VA_OPT__(test)__VA_OPT__(fred)
foo30()
foo30(test)

#define foo31(__VA_OPT__) #__VA_OPT__
foo31(foo)
#define foo32(__VA_OPT__, ...) #__VA_OPT__()
foo32(foo)

#define foo33(...) __VA_OPT__(fred)
#define foo33(...) __VA_OPT__ (fred)

#define foo34(...) __VA_OPT__ (a )
#define foo34(...) __VA_OPT__ ( a)

#define foo35(...) __VA_OPT__ (+a)
#define foo35(...) __VA_OPT__ (+ a)

#define foo36(...) __VA_OPT__ (a b)
#define foo36(...) __VA_OPT__(a) __VA_OPT__(b)

#define foo37(a, ...) start __VA_OPT__(,) end
foo37(foo)
foo37(foo,)
foo37(foo,bar)

/* https://gcc.gnu.org/bugzilla/show_bug.cgi?id=83063 */
#define ice(...) b##__VA_OPT__ ()
ice ()
@mycoboco

This comment has been minimized.

Show comment
Hide comment
@mycoboco

mycoboco Jan 3, 2018

Owner

git diff --no-prefix of an incomplete implementation:

diff --git lib/mcr.c lib/mcr.c
index 683c889..a3c80a8 100644
--- lib/mcr.c
+++ lib/mcr.c
@@ -510,13 +510,13 @@ static struct mtab *conflict(const char *chn)
 lex_t *(mcr_define)(const lmap_t *pos, int cmd)
 {
     int n = -1;
-    int sharp = 0;
     lex_t *t, *pt, *v, *l;
     const char *cn, *s;
-    const lmap_t *idpos;
+    const lmap_t *idpos, *lpos;
     arena_t *strg;
     lex_t **param = NULL;
     struct pel *pe = NULL;
+    int opt = 0, sharp = 0;
 
     NEXTSP(t);    /* consumes define */
     if (t->id != LEX_ID) {
@@ -597,18 +597,41 @@ lex_t *(mcr_define)(const lmap_t *pos, int cmd)
         if (t->id == LEX_SPACE) {
             lex_t *u;
             NEXTSP(u);    /* consumes space */
-            if (u->id != LEX_NEWLINE && u->id != LEX_EOI) {
+            if (u->id != LEX_NEWLINE && u->id != LEX_EOI && !(opt == 1 && u->id == ')')) {
                 SPELL(t, " ");
-                l = lst_append(l, lst_copy(t, 0, strg));
+                l = lst_append(l, lst_copy(t, 0, strg)), l->f.vaopt = (opt > 0);
             }
             t = u;
             continue;
         }
-        if (t->id == LEX_ID && t->spell[0] == '_' && !v) {    /* before copy */
+        if (t->id == LEX_ID && t->spell[0] == '_') {    /* before copy */
             s = LEX_SPELL(t);
-            MCR_IDVAARGS(s, t);
+            if (!v)
+                MCR_IDVAARGS(s, t);
+            else if (MCR_ISVAOPT(s)) {
+                if (opt > 0) {
+                    return t;
+                }
+                NEXTSP(t);    /* consumes __VA_OPT__ */
+                if (t->id == '(') {
+                    opt = 1;
+                    lpos = t->pos;
+                    NEXTSP(t);    /* consumes ( */
+                    if (t->id == LEX_DSHARP) {
+                        err_dpos(t->pos, ERR_PP_DSHARPPOS, "__VA_OPT__");
+                        return t;
+                    }
+                } else {
+                    err_dpos(t->pos, ERR_PP_);
+                    return t;
+                }
+            }
+        } else if (opt == 1 && t->id == ')') {
+            opt = 0;
+            t = lst_nexti();
+            continue;
         }
-        l = lst_append(l, lst_copy(t, 0, strg));
+        l = lst_append(l, lst_copy(t, 0, strg)), l->f.vaopt = (opt > 0);
         if (n > 0 && t->id == LEX_ID) {
             struct pel *p = pelookup(pe, t);
             if (p)
@@ -621,13 +644,13 @@ lex_t *(mcr_define)(const lmap_t *pos, int cmd)
                 NEXTSP(u);    /* consumes space */
                 if (u->id != LEX_NEWLINE && u->id != LEX_EOI) {
                     SPELL(t, " ");
-                    l = lst_append(l, lst_copy(t, 0, strg));
+                    l = lst_append(l, lst_copy(t, 0, strg)), l->f.vaopt = (opt > 0);
                 }
                 t = u;
             }
             if (ts->id == LEX_DSHARP) {
                 if (l->next->id == LEX_DSHARP || (t->id == LEX_NEWLINE || t->id == LEX_EOI)) {
-                    err_dpos(ts->pos, ERR_PP_DSHARPPOS);
+                    err_dpos(ts->pos, ERR_PP_DSHARPPOS, "macro expansion");
                     return t;
                 } else if (t->id == LEX_DSHARP) {
                     err_dpos(t->pos, ERR_PP_TWODSHARP);
@@ -653,11 +676,17 @@ lex_t *(mcr_define)(const lmap_t *pos, int cmd)
             }
             pt = ts;
             continue;
+        } else if (opt > 0) {
+            if (t->id == '(')
+                opt++;
+            else if (t->id == ')')
+                opt--;
         }
         pt = l;    /* not t */
         t = lst_nexti();
     }
-
+    if (opt > 0)
+        ;
     {    /* installation */
         struct mtab *p;
 
diff --git lib/mcr.h lib/mcr.h
index 15fd861..c4dc41d 100644
--- lib/mcr.h
+++ lib/mcr.h
@@ -30,15 +30,20 @@ void mcr_free(void);
 #define mcr_addcmd(a) (mcr_cmd(0, (a)))
 #define mcr_delcmd(a) (mcr_cmd(1, (a)))
 
-/* checks if __VA_ARGS__ */
-#define MCR_ISVAARGS(s)    \
-    ((s)[0] == '_' && (s)[1] == '_' && (s)[2] == 'V' && strcmp((s)+3, "A_ARGS__") == 0)
+/* checks if __VA_ARGS__ or __VA_OPT__ */
+#define MCR_ISVA(s)    \
+    ((s)[0] == '_' && (s)[1] == '_' && (s)[2] == 'V' && (s)[3] == 'A' && (s)[4] == '_')
+#define MCR_ISVAARGS(s) (MCR_ISVA(s) && (s)[5] == 'A' && strcmp((s)+6, "RGS__") == 0)
+#define MCR_ISVAOPT(s)  (MCR_ISVA(s) && (s)[5] == 'O' && strcmp((s)+6, "PT__") == 0)
+#define MCR_ISVAS(s)                                                      \
+    (MCR_ISVA(s) && (((s)[5] == 'A' && strcmp((s)+6, "RGS__") == 0) ||    \
+                     ((s)[5] == 'O' && strcmp((s)+6, "PT__") == 0)))
 
 /* issues diagnostics when __VA_ARGS__ encountered */
-#define MCR_IDVAARGS(s, t)                                          \
-    do {                                                            \
-        if (MCR_ISVAARGS(s) && !(t)->f.vaarg)                       \
-            err_dpos((t)->pos, ERR_PP_VAARGS), (t)->f.vaarg = 1;    \
+#define MCR_IDVAARGS(s, t)                                            \
+    do {                                                              \
+        if (MCR_ISVAS(s) && !(t)->f.vaarg)                            \
+            err_dpos((t)->pos, ERR_PP_VAS, (s)), (t)->f.vaarg = 1;    \
     } while(0)
 
 
diff --git lib/xerror.h lib/xerror.h
index 074e71b..a7c7618 100644
--- lib/xerror.h
+++ lib/xerror.h
@@ -48,7 +48,7 @@ xx(PP_PMCRREDEF,      E|P        , 0, "redefinition of built-in macro `%s'"
 xx(PP_PMCRUNDEF,      E|P        , 0, "undefining built-in macro `%s'"                             )
 xx(PP_UNDEFMCR,         P        , 4, "#undefining undefined macro `%s'"                           )
 xx(PP_ELLSEEN,        E|P        , 0, "`...' must be the last in parameters"                       )
-xx(PP_VAARGS,           P        , 0, "__VA_ARGS__ can appear only in variadic replacement list"   )
+xx(PP_VAS,              P        , 0, "%s can appear only in variadic replacement list"            )
 xx(PP_VARIADIC,         P  |A    , 3, "C90 does not support variadic macros"                       )
 xx(PP_NOPNAME,        E|P        , 0, "missing identifier for macro parameter name"                )
 xx(PP_NOPRPAREN,      E|P        , 0, "missing `)' in macro parameter list"                        )
@@ -59,7 +59,7 @@ xx(PP_MANYPARAM,        P        , 2, "too many parameters"
 xx(PP_MANYPSTD,       N    |A|B|C, 3, "ISO C guarantees only %d parameters"                        )
 xx(PP_MANYPPID,         P        , 2, "too many macros simultaneously defined"                     )
 xx(PP_MANYPPIDSTD,    N    |A|B|C, 3, "ISO C guarantees only %d macros"                            )
-xx(PP_DSHARPPOS,      E|P        , 0, "`##' cannot appear at the boundaries of macro expansion"    )
+xx(PP_DSHARPPOS,      E|P        , 0, "`##' cannot appear at the boundaries of %s"                 )
 xx(PP_TWODSHARP,      E|P        , 0, "`##' cannot be an operand of `##'"                          )
 xx(PP_NEEDPARAM,      E|P        , 0, "`#' must be followed by a macro parameter"                  )
 xx(PP_EMPTYARG,         P  |A    , 3, "C90 does not support empty argument to macro `%s'"          )
Owner

mycoboco commented Jan 3, 2018

git diff --no-prefix of an incomplete implementation:

diff --git lib/mcr.c lib/mcr.c
index 683c889..a3c80a8 100644
--- lib/mcr.c
+++ lib/mcr.c
@@ -510,13 +510,13 @@ static struct mtab *conflict(const char *chn)
 lex_t *(mcr_define)(const lmap_t *pos, int cmd)
 {
     int n = -1;
-    int sharp = 0;
     lex_t *t, *pt, *v, *l;
     const char *cn, *s;
-    const lmap_t *idpos;
+    const lmap_t *idpos, *lpos;
     arena_t *strg;
     lex_t **param = NULL;
     struct pel *pe = NULL;
+    int opt = 0, sharp = 0;
 
     NEXTSP(t);    /* consumes define */
     if (t->id != LEX_ID) {
@@ -597,18 +597,41 @@ lex_t *(mcr_define)(const lmap_t *pos, int cmd)
         if (t->id == LEX_SPACE) {
             lex_t *u;
             NEXTSP(u);    /* consumes space */
-            if (u->id != LEX_NEWLINE && u->id != LEX_EOI) {
+            if (u->id != LEX_NEWLINE && u->id != LEX_EOI && !(opt == 1 && u->id == ')')) {
                 SPELL(t, " ");
-                l = lst_append(l, lst_copy(t, 0, strg));
+                l = lst_append(l, lst_copy(t, 0, strg)), l->f.vaopt = (opt > 0);
             }
             t = u;
             continue;
         }
-        if (t->id == LEX_ID && t->spell[0] == '_' && !v) {    /* before copy */
+        if (t->id == LEX_ID && t->spell[0] == '_') {    /* before copy */
             s = LEX_SPELL(t);
-            MCR_IDVAARGS(s, t);
+            if (!v)
+                MCR_IDVAARGS(s, t);
+            else if (MCR_ISVAOPT(s)) {
+                if (opt > 0) {
+                    return t;
+                }
+                NEXTSP(t);    /* consumes __VA_OPT__ */
+                if (t->id == '(') {
+                    opt = 1;
+                    lpos = t->pos;
+                    NEXTSP(t);    /* consumes ( */
+                    if (t->id == LEX_DSHARP) {
+                        err_dpos(t->pos, ERR_PP_DSHARPPOS, "__VA_OPT__");
+                        return t;
+                    }
+                } else {
+                    err_dpos(t->pos, ERR_PP_);
+                    return t;
+                }
+            }
+        } else if (opt == 1 && t->id == ')') {
+            opt = 0;
+            t = lst_nexti();
+            continue;
         }
-        l = lst_append(l, lst_copy(t, 0, strg));
+        l = lst_append(l, lst_copy(t, 0, strg)), l->f.vaopt = (opt > 0);
         if (n > 0 && t->id == LEX_ID) {
             struct pel *p = pelookup(pe, t);
             if (p)
@@ -621,13 +644,13 @@ lex_t *(mcr_define)(const lmap_t *pos, int cmd)
                 NEXTSP(u);    /* consumes space */
                 if (u->id != LEX_NEWLINE && u->id != LEX_EOI) {
                     SPELL(t, " ");
-                    l = lst_append(l, lst_copy(t, 0, strg));
+                    l = lst_append(l, lst_copy(t, 0, strg)), l->f.vaopt = (opt > 0);
                 }
                 t = u;
             }
             if (ts->id == LEX_DSHARP) {
                 if (l->next->id == LEX_DSHARP || (t->id == LEX_NEWLINE || t->id == LEX_EOI)) {
-                    err_dpos(ts->pos, ERR_PP_DSHARPPOS);
+                    err_dpos(ts->pos, ERR_PP_DSHARPPOS, "macro expansion");
                     return t;
                 } else if (t->id == LEX_DSHARP) {
                     err_dpos(t->pos, ERR_PP_TWODSHARP);
@@ -653,11 +676,17 @@ lex_t *(mcr_define)(const lmap_t *pos, int cmd)
             }
             pt = ts;
             continue;
+        } else if (opt > 0) {
+            if (t->id == '(')
+                opt++;
+            else if (t->id == ')')
+                opt--;
         }
         pt = l;    /* not t */
         t = lst_nexti();
     }
-
+    if (opt > 0)
+        ;
     {    /* installation */
         struct mtab *p;
 
diff --git lib/mcr.h lib/mcr.h
index 15fd861..c4dc41d 100644
--- lib/mcr.h
+++ lib/mcr.h
@@ -30,15 +30,20 @@ void mcr_free(void);
 #define mcr_addcmd(a) (mcr_cmd(0, (a)))
 #define mcr_delcmd(a) (mcr_cmd(1, (a)))
 
-/* checks if __VA_ARGS__ */
-#define MCR_ISVAARGS(s)    \
-    ((s)[0] == '_' && (s)[1] == '_' && (s)[2] == 'V' && strcmp((s)+3, "A_ARGS__") == 0)
+/* checks if __VA_ARGS__ or __VA_OPT__ */
+#define MCR_ISVA(s)    \
+    ((s)[0] == '_' && (s)[1] == '_' && (s)[2] == 'V' && (s)[3] == 'A' && (s)[4] == '_')
+#define MCR_ISVAARGS(s) (MCR_ISVA(s) && (s)[5] == 'A' && strcmp((s)+6, "RGS__") == 0)
+#define MCR_ISVAOPT(s)  (MCR_ISVA(s) && (s)[5] == 'O' && strcmp((s)+6, "PT__") == 0)
+#define MCR_ISVAS(s)                                                      \
+    (MCR_ISVA(s) && (((s)[5] == 'A' && strcmp((s)+6, "RGS__") == 0) ||    \
+                     ((s)[5] == 'O' && strcmp((s)+6, "PT__") == 0)))
 
 /* issues diagnostics when __VA_ARGS__ encountered */
-#define MCR_IDVAARGS(s, t)                                          \
-    do {                                                            \
-        if (MCR_ISVAARGS(s) && !(t)->f.vaarg)                       \
-            err_dpos((t)->pos, ERR_PP_VAARGS), (t)->f.vaarg = 1;    \
+#define MCR_IDVAARGS(s, t)                                            \
+    do {                                                              \
+        if (MCR_ISVAS(s) && !(t)->f.vaarg)                            \
+            err_dpos((t)->pos, ERR_PP_VAS, (s)), (t)->f.vaarg = 1;    \
     } while(0)
 
 
diff --git lib/xerror.h lib/xerror.h
index 074e71b..a7c7618 100644
--- lib/xerror.h
+++ lib/xerror.h
@@ -48,7 +48,7 @@ xx(PP_PMCRREDEF,      E|P        , 0, "redefinition of built-in macro `%s'"
 xx(PP_PMCRUNDEF,      E|P        , 0, "undefining built-in macro `%s'"                             )
 xx(PP_UNDEFMCR,         P        , 4, "#undefining undefined macro `%s'"                           )
 xx(PP_ELLSEEN,        E|P        , 0, "`...' must be the last in parameters"                       )
-xx(PP_VAARGS,           P        , 0, "__VA_ARGS__ can appear only in variadic replacement list"   )
+xx(PP_VAS,              P        , 0, "%s can appear only in variadic replacement list"            )
 xx(PP_VARIADIC,         P  |A    , 3, "C90 does not support variadic macros"                       )
 xx(PP_NOPNAME,        E|P        , 0, "missing identifier for macro parameter name"                )
 xx(PP_NOPRPAREN,      E|P        , 0, "missing `)' in macro parameter list"                        )
@@ -59,7 +59,7 @@ xx(PP_MANYPARAM,        P        , 2, "too many parameters"
 xx(PP_MANYPSTD,       N    |A|B|C, 3, "ISO C guarantees only %d parameters"                        )
 xx(PP_MANYPPID,         P        , 2, "too many macros simultaneously defined"                     )
 xx(PP_MANYPPIDSTD,    N    |A|B|C, 3, "ISO C guarantees only %d macros"                            )
-xx(PP_DSHARPPOS,      E|P        , 0, "`##' cannot appear at the boundaries of macro expansion"    )
+xx(PP_DSHARPPOS,      E|P        , 0, "`##' cannot appear at the boundaries of %s"                 )
 xx(PP_TWODSHARP,      E|P        , 0, "`##' cannot be an operand of `##'"                          )
 xx(PP_NEEDPARAM,      E|P        , 0, "`#' must be followed by a macro parameter"                  )
 xx(PP_EMPTYARG,         P  |A    , 3, "C90 does not support empty argument to macro `%s'"          )
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment