aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorlemon <lsof@mailbox.org>2026-03-24 20:13:28 +0100
committerlemon <lsof@mailbox.org>2026-03-24 20:13:28 +0100
commit803dbff03b7c30f276f6b8923b1c6b0a28e7b4f6 (patch)
tree13b7c0576e313b4e63318055bcbc5ff9667f60f8
parentfbb7bd3e9a67a0390c7de5667a8141fcbb1d58e2 (diff)
cpp: support directives within macro argument list
Undefined behavior by the standard but a GNU extension.
-rw-r--r--src/c_lex.c135
-rw-r--r--src/u_io.c2
-rw-r--r--test/07-pp.c11
3 files changed, 93 insertions, 55 deletions
diff --git a/src/c_lex.c b/src/c_lex.c
index e986fca..70439a9 100644
--- a/src/c_lex.c
+++ b/src/c_lex.c
@@ -1141,6 +1141,8 @@ advancemacstk(Lexer *lx, Token *tk)
return tryexpand(lx, tk) != EXPSTACK;
}
+static int ppdirective(Lexer *lx, bool *skip, bool *inclerror);
+
static void
expandfnmacro(Lexer *lx, Span *span, internstr mname, Macro *mac)
{
@@ -1159,19 +1161,27 @@ expandfnmacro(Lexer *lx, Span *span, internstr mname, Macro *mac)
*args = mac->nparam < countof(_args0) ? _args0 : alloc(lx->tmparena, sizeof *args * mac->nparam, 0);
cur = i = bal = len = narg = 0;
+ bool cndskip = 0;
for (MacroStack *s = lx->macstk;;) {
if (!s) {
bool nl = 0;
+ Linebegin:
for (;; nl = 1) {
lex0(lx, &tk, 0);
if (tk.t != '\n') break;
}
tk.space |= nl;
- }
- else {
+ if (nl && tk.t == '#') {
+ /* extension: embedding directive within a macro argument */
+ ppdirective(lx, &cndskip, NULL);
+ goto Linebegin;
+ }
+ } else {
tk = s->idx < s->rl.n ? stkgetrl(s)[s->idx++] : (Token){TKEOF};
}
- if (((tk.t == ')' && bal == 0) || tk.t == TKEOF)) break;
+ if (tk.t == TKEOF) break;
+ if (cndskip) continue;
+ if (((tk.t == ')' && bal == 0))) break;
if (tk.t == ',' && bal == 0) {
++narg;
if (i == mac->nparam-1 && !mac->variadic) {
@@ -2086,6 +2096,72 @@ identkeyword(Token *tk)
}
}
+static int
+ppdirective(Lexer *lx, bool *skip, bool *inclerror)
+{
+ Token tk[1];
+ enum directive lastcmd = 0;
+
+ if (lex0(lx, tk, 0) == '\n') { }
+ else if (tk->t == TKNUMLIT || tk->t == TKIDENT) {
+ lastcmd = tk->t == TKNUMLIT ? PPLINE : findppcmd(tk);
+ if (nppcnd == lx->nppcnd0) lx->inclguard = NULL;
+ if (!*skip) {
+ switch (lastcmd) {
+ case PPXXX: goto BadPP;
+ case PPDEFINE: ppdefine(lx); break;
+ case PPUNDEF: ppundef(lx); break;
+ case PPIF: ppif(lx, &tk->span); break;
+ case PPIFDEF: ppifxdef(lx, 1, &tk->span); break;
+ case PPIFNDEF: ppifxdef(lx, 0, &tk->span); break;
+ case PPELIF: ppelif(lx, &tk->span); break;
+ case PPELIFDEF: ppelifxdef(lx, 1, &tk->span); break;
+ case PPELIFNDEF: ppelifxdef(lx, 0, &tk->span); break;
+ case PPELSE: ppelse(lx, &tk->span); break;
+ case PPENDIF: ppendif(lx, &tk->span); break;
+ case PPLINE: ppline(lx, tk); break;
+ case PPPRAGMA: pppragma(lx, &tk->span); break;
+ case PPWARNING: ppdiag(lx, &tk->span, 0); break;
+ case PPERROR: ppdiag(lx, &tk->span, 1); break;
+ case PPINCLUDE: if (!inclerror)
+ error(&tk->span, "#include directive within macro argument");
+ else
+ *inclerror |= !ppinclude(lx, &tk->span);
+ break;
+ default: assert(0&&"nyi");
+ }
+ } else {
+ switch (lastcmd) {
+ case PPIF: /* increment nesting level */
+ case PPIFDEF:
+ case PPIFNDEF:
+ assert(nppcnd < countof(ppcndstk) && "too many nested #if");
+ ppcndstk[nppcnd].ifspan = tk->span.sl;
+ ppcndstk[nppcnd].cnd = PPCNDTAKEN;
+ ppcndstk[nppcnd++].elsep = 0;
+ break;
+ case PPELIF: ppelif(lx, &tk->span); break;
+ case PPELIFDEF: ppelifxdef(lx, 1, &tk->span); break;
+ case PPELIFNDEF: ppelifxdef(lx, 0, &tk->span); break;
+ case PPELSE: ppelse(lx, &tk->span); break;
+ case PPENDIF: ppendif(lx, &tk->span); break;
+ default: ppskipline(lx); break;
+ }
+ }
+ if (lastcmd != PPINCLUDE)
+ lx->firstdirective = 0;
+ *skip = nppcnd ? ppcndstk[nppcnd-1].cnd != PPCNDTRUE : 0;
+ } else {
+ if (!skip) {
+ BadPP:
+ error(&tk->span, "invalid preprocessor directive");
+ }
+ ppskipline(lx);
+ }
+
+ return lastcmd;
+}
+
int
lex(Lexer *lx, Token *tk_)
{
@@ -2114,59 +2190,8 @@ Begin:
for (;;) {
while ((t = lex0(lx, tk, 0)) == '\n') linebegin = 1;
if (t == '#' && linebegin) {
- if (lex0(lx, tk, 0) == '\n') { }
- else if (tk->t == TKNUMLIT || tk->t == TKIDENT) {
- lastcmd = tk->t == TKNUMLIT ? PPLINE : findppcmd(tk);
- if (nppcnd == lx->nppcnd0) lx->inclguard = NULL;
- if (!skip) {
- switch (lastcmd) {
- case PPXXX: goto BadPP;
- case PPDEFINE: ppdefine(lx); break;
- case PPUNDEF: ppundef(lx); break;
- case PPIF: ppif(lx, &tk->span); break;
- case PPIFDEF: ppifxdef(lx, 1, &tk->span); break;
- case PPIFNDEF: ppifxdef(lx, 0, &tk->span); break;
- case PPELIF: ppelif(lx, &tk->span); break;
- case PPELIFDEF: ppelifxdef(lx, 1, &tk->span); break;
- case PPELIFNDEF: ppelifxdef(lx, 0, &tk->span); break;
- case PPELSE: ppelse(lx, &tk->span); break;
- case PPENDIF: ppendif(lx, &tk->span); break;
- case PPLINE: ppline(lx, tk); break;
- case PPPRAGMA: pppragma(lx, &tk->span); break;
- case PPWARNING: ppdiag(lx, &tk->span, 0); break;
- case PPERROR: ppdiag(lx, &tk->span, 1); break;
- case PPINCLUDE: inclerror |= !ppinclude(lx, &tk->span); break;
- default: assert(0&&"nyi");
- }
- } else {
- switch (lastcmd) {
- case PPIF: /* increment nesting level */
- case PPIFDEF:
- case PPIFNDEF:
- assert(nppcnd < countof(ppcndstk) && "too many nested #if");
- ppcndstk[nppcnd].ifspan = tk->span.sl;
- ppcndstk[nppcnd].cnd = PPCNDTAKEN;
- ppcndstk[nppcnd++].elsep = 0;
- break;
- case PPELIF: ppelif(lx, &tk->span); break;
- case PPELIFDEF: ppelifxdef(lx, 1, &tk->span); break;
- case PPELIFNDEF: ppelifxdef(lx, 0, &tk->span); break;
- case PPELSE: ppelse(lx, &tk->span); break;
- case PPENDIF: ppendif(lx, &tk->span); break;
- default: ppskipline(lx); break;
- }
- }
- if (lastcmd != PPINCLUDE)
- lx->firstdirective = 0;
- skip = nppcnd ? ppcndstk[nppcnd-1].cnd != PPCNDTRUE : 0;
- } else {
- if (!skip) {
- BadPP:
- error(&tk->span, "invalid preprocessor directive");
- }
- ppskipline(lx);
- }
linebegin = 1;
+ lastcmd = ppdirective(lx, &skip, &inclerror);
} else {
lx->firstdirective = 0;
linebegin = 0;
diff --git a/src/u_io.c b/src/u_io.c
index 1dccb9d..2eada7e 100644
--- a/src/u_io.c
+++ b/src/u_io.c
@@ -562,6 +562,8 @@ vbfmt(WriteBuf *out, const char *fmt, va_list ap)
n += tok->len;
} else if (aisprint(tok->t)) {
n += bputc(buf, tok->t);
+ } else if (tok->t == '\n') {
+ n += bwriteS(buf, "<newline>");
} else {
n += bwriteS(buf, "??");
}
diff --git a/test/07-pp.c b/test/07-pp.c
index 47fb959..4301dbd 100644
--- a/test/07-pp.c
+++ b/test/07-pp.c
@@ -14,6 +14,7 @@ token spacing:
sum = 1 + 2 +3;$
[ baz] ;$
+. *$
+x=7
*/
#include "07-pp.h"
@@ -129,5 +130,15 @@ S\
qx2(*) ) "$\n"
);
+#define A(x,y) x=y
+ A(int x,
+#if 1
+ 7
+#else
+ 3
+#endif
+ );
+ printf("x=%d\n",x);
+
CAT(ret,urn) 0;
}