From 2f90db434e68b79ef6dab3a25d752ecca1bf00e2 Mon Sep 17 00:00:00 2001 From: Philip Date: Sat, 2 May 2015 14:27:49 +0000 Subject: [PATCH] tccpp.c: fix GNU comma handling This requires moving TOK_PLCHLDR handling, but the new logic should make things easier even if (when?) GNU comma handling is removed. (Somewhat confusingly, GCC no longer supports GNU commas. See http://gcc.gnu.org/onlinedocs/cpp/Variadic-Macros.html for a description of past and current GCC behaviour.) --- tcc.h | 1 + tccpp.c | 53 ++++++++++++++++++++++++++++++++++------------------- 2 files changed, 35 insertions(+), 19 deletions(-) diff --git a/tcc.h b/tcc.h index a0d0635a..8d832fff 100644 --- a/tcc.h +++ b/tcc.h @@ -880,6 +880,7 @@ struct TCCState { #define TOK_DOTS 0xcc /* three dots */ #define TOK_SHR 0xcd /* unsigned shift right */ #define TOK_NOSUBST 0xcf /* means following token has already been pp'd */ +#define TOK_GNUCOMMA 0xd0 /* ,## preprocessing token */ #define TOK_SHL 0x01 /* shift left */ #define TOK_SAR 0x02 /* signed shift right */ diff --git a/tccpp.c b/tccpp.c index 7dcce6c0..e06f7dd6 100644 --- a/tccpp.c +++ b/tccpp.c @@ -2786,25 +2786,18 @@ static int *macro_arg_subst(Sym **nested_list, const int *macro_str, Sym *args) if (gnu_ext && s->type.t && last_tok == TOK_TWOSHARPS && str.len >= 2 && str.str[str.len - 2] == ',') { - if (*st == TOK_PLCHLDR) { - /* suppress ',' '##' */ - str.len -= 2; - } else { - /* suppress '##' and add variable */ - str.len--; - goto add_var; - } - } else { + str.len -= 2; + tok_str_add(&str, TOK_GNUCOMMA); + } + + for(;;) { int t1; - add_var: - for(;;) { - TOK_GET(&t1, &st, &cval); - if (!t1) - break; - tok_str_add2(&str, t1, &cval); - } + TOK_GET(&t1, &st, &cval); + if (!t1) + break; + tok_str_add2(&str, t1, &cval); } - } else if (*st != TOK_PLCHLDR) { + } else { /* NOTE: the stream cannot be read when macro substituing an argument */ macro_subst(&str, nested_list, st, NULL); @@ -2817,6 +2810,8 @@ static int *macro_arg_subst(Sym **nested_list, const int *macro_str, Sym *args) } last_tok = t; } + if (str.len == 0) + tok_str_add(&str, TOK_PLCHLDR); tok_str_add(&str, 0); return str.str; } @@ -2982,9 +2977,9 @@ static int macro_subst_tok(TokenString *tok_str, tok_str_add2(&str, tok, &tokc); next_nomacro_spc(); } - if (!str.len) - tok_str_add(&str, TOK_PLCHLDR); str.len -= spc; + if (str.len == 0) + tok_str_add(&str, TOK_PLCHLDR); tok_str_add(&str, 0); sa1 = sym_push2(&args, sa->v & ~SYM_FIELD, sa->type.t, 0); sa1->d = str.str; @@ -3123,6 +3118,7 @@ static void macro_subst(TokenString *tok_str, Sym **nested_list, CValue cval; struct macro_level ml; int force_blank; + int gnucomma_index = -1; /* first scan for '##' operator handling */ ptr = macro_str; @@ -3150,8 +3146,16 @@ static void macro_subst(TokenString *tok_str, Sym **nested_list, TOK_GET(&t, &ptr, &cval); goto no_subst; } + if (t == TOK_GNUCOMMA) { + if (gnucomma_index != -1) + tcc_error("two GNU commas in the same macro"); + gnucomma_index = tok_str->len; + tok_str_add(tok_str, ','); + TOK_GET(&t, &ptr, &cval); + } s = define_find(t); if (s != NULL) { + int old_len = tok_str->len; /* if nested substitution, do nothing */ if (sym_find2(*nested_list, t)) { /* and mark it as TOK_NOSUBST, so it doesn't get subst'd again */ @@ -3171,6 +3175,8 @@ static void macro_subst(TokenString *tok_str, Sym **nested_list, *can_read_stream = ml.prev; if (parse_flags & PARSE_FLAG_SPACES) force_blank = 1; + if (old_len == tok_str->len) + tok_str_add(tok_str, TOK_PLCHLDR); } else { no_subst: if (force_blank) { @@ -3181,6 +3187,15 @@ static void macro_subst(TokenString *tok_str, Sym **nested_list, if (!check_space(t, &spc)) tok_str_add2(tok_str, t, &cval); } + if (gnucomma_index != -1) { + if (tok_str->len >= gnucomma_index+2) { + if (tok_str->str[gnucomma_index+1] == TOK_PLCHLDR) + tok_str->len -= 2; + gnucomma_index = -1; + } + } + if (tok_str->len && tok_str->str[tok_str->len-1] == TOK_PLCHLDR) + tok_str->len--; } if (macro_str1) tok_str_free(macro_str1); -- 2.11.4.GIT