mycoboco / beluga

a standard C compiler (with an integrated preprocessor)
http://code.woong.org/beluga
Other
65 stars 8 forks source link

implement __VA_OPT__ #135

Open mycoboco opened 6 years ago

mycoboco commented 6 years ago

Cases to handle:

mycoboco commented 6 years ago

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

mycoboco commented 6 years ago

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 commented 6 years ago

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'"          )