beluga
beluga copied to clipboard
implement __VA_OPT__
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)
- [ ] space between
- avoid token paste
- [ ]
__VA_OPT__(foo)bar
- [ ]
__VA_OPT__(foo)__VA_OPT__(bar)
- [ ]
Has so many corner cases that it seems too early to have a solid implementation.
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 ()
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'" )