diff options
| author | nsfisis <nsfisis@gmail.com> | 2026-01-14 23:12:49 +0900 |
|---|---|---|
| committer | nsfisis <nsfisis@gmail.com> | 2026-01-17 00:27:28 +0900 |
| commit | f0351da192b8f32bdf9323b3796521cbc390c749 (patch) | |
| tree | 789fbf7d954a2dd43bf01c80d1ae959a2a807729 | |
| parent | 82df1b08c42d40c79b0dbd1634e18786cad860da (diff) | |
| download | ducc-f0351da192b8f32bdf9323b3796521cbc390c749.tar.gz ducc-f0351da192b8f32bdf9323b3796521cbc390c749.tar.zst ducc-f0351da192b8f32bdf9323b3796521cbc390c749.zip | |
feat: support #include_next directive (GNU extension)
| -rw-r--r-- | src/preprocess.c | 51 | ||||
| -rw-r--r-- | src/token.c | 7 | ||||
| -rw-r--r-- | src/token.h | 1 | ||||
| -rw-r--r-- | src/tokenize.c | 5 |
4 files changed, 60 insertions, 4 deletions
diff --git a/src/preprocess.c b/src/preprocess.c index 54e3648..869b069 100644 --- a/src/preprocess.c +++ b/src/preprocess.c @@ -330,7 +330,7 @@ static void make_tokens_removed(Preprocessor* pp, int start, int end) { static Token* read_include_header_name(Preprocessor* pp) { Token* tok = next_pp_token(pp); if (tok->kind != TokenKind_header_name) { - fatal_error("%s:%d: invalid #include", tok->loc.filename, tok->loc.line); + fatal_error("%s:%d: invalid #include, %s", tok->loc.filename, tok->loc.line, token_stringify(tok)); } return tok; } @@ -355,6 +355,23 @@ static const char* resolve_include_name(Preprocessor* pp, const Token* include_n } } +static const char* resolve_next_include_name(Preprocessor* pp, const Token* include_name_token) { + const char* include_name = include_name_token->value.string; + char* current_filename = strdup(include_name_token->loc.filename); + for (size_t i = 0; i < pp->include_paths.len; ++i) { + char* buf = calloc(strlen(include_name) - 2 + 1 + strlen(pp->include_paths.data[i]) + 1, sizeof(char)); + sprintf(buf, "%s/%.*s", pp->include_paths.data[i], (int)(strlen(include_name) - 2), include_name + 1); + if (strcmp(buf, current_filename) == 0) { + // #include_next skips the same file. + continue; + } + if (access(buf, F_OK | R_OK) == 0) { + return buf; + } + } + return NULL; +} + static int replace_pp_tokens(Preprocessor* pp, int dest_start, int dest_end, TokenArray* source_tokens) { size_t n_tokens_to_remove = dest_end - dest_start; size_t n_tokens_after_dest = pp->pp_tokens->len - dest_end; @@ -1061,6 +1078,36 @@ static void preprocess_include_directive(Preprocessor* pp) { expand_include_directive(pp, include_name_resolved, include_name); } +// #include_next is a part of GNU extension. +// https://gcc.gnu.org/onlinedocs/cpp/Wrapper-Headers.html +static void preprocess_include_next_directive(Preprocessor* pp) { + skip_pp_token(pp, TokenKind_pp_directive_include_next); + skip_whitespaces(pp); + Token* include_name = read_include_header_name(pp); + const char* include_name_resolved = resolve_next_include_name(pp, include_name); + if (include_name_resolved == NULL) { + fatal_error("%s:%d: cannot resolve include file name: %s", include_name->loc.filename, include_name->loc.line, + token_stringify(include_name)); + } + + if (include_name->value.string[0] == '"') { + bool already_included = false; + for (size_t i = 0; i < pp->included_files->len; ++i) { + if (strcmp(pp->included_files->data[i], include_name_resolved) == 0) { + already_included = true; + break; + } + } + if (!already_included) { + strings_push(pp->included_files, include_name_resolved); + } + } + + skip_whitespaces(pp); + expect_pp_token(pp, TokenKind_newline); + expand_include_directive(pp, include_name_resolved, include_name); +} + static void preprocess_embed_directive(Preprocessor*) { unimplemented(); } @@ -1222,6 +1269,8 @@ static void preprocess_group_part(Preprocessor* pp) { token_kind_stringify(tok->kind)); } else if (tok->kind == TokenKind_pp_directive_include) { preprocess_include_directive(pp); + } else if (tok->kind == TokenKind_pp_directive_include_next) { + preprocess_include_next_directive(pp); } else if (tok->kind == TokenKind_pp_directive_embed) { preprocess_embed_directive(pp); } else if (tok->kind == TokenKind_pp_directive_define) { diff --git a/src/token.c b/src/token.c index ebb803e..a7b2afd 100644 --- a/src/token.c +++ b/src/token.c @@ -47,6 +47,8 @@ const char* token_kind_stringify(TokenKind k) { return "#ifndef"; else if (k == TokenKind_pp_directive_include) return "#include"; + else if (k == TokenKind_pp_directive_include_next) + return "#include_next"; else if (k == TokenKind_pp_directive_line) return "#line"; else if (k == TokenKind_pp_directive_non_directive) @@ -283,8 +285,9 @@ bool is_pp_directive(TokenKind k) { k == TokenKind_pp_directive_else || k == TokenKind_pp_directive_embed || k == TokenKind_pp_directive_endif || k == TokenKind_pp_directive_error || k == TokenKind_pp_directive_if || k == TokenKind_pp_directive_ifdef || k == TokenKind_pp_directive_ifndef || k == TokenKind_pp_directive_include || - k == TokenKind_pp_directive_line || k == TokenKind_pp_directive_non_directive || - k == TokenKind_pp_directive_nop || k == TokenKind_pp_directive_pragma || k == TokenKind_pp_directive_undef || + k == TokenKind_pp_directive_include_next || k == TokenKind_pp_directive_line || + k == TokenKind_pp_directive_non_directive || k == TokenKind_pp_directive_nop || + k == TokenKind_pp_directive_pragma || k == TokenKind_pp_directive_undef || k == TokenKind_pp_directive_warning; } diff --git a/src/token.h b/src/token.h index e093a8c..b81c10a 100644 --- a/src/token.h +++ b/src/token.h @@ -29,6 +29,7 @@ typedef enum { TokenKind_pp_directive_ifdef, TokenKind_pp_directive_ifndef, TokenKind_pp_directive_include, + TokenKind_pp_directive_include_next, // GNU extension TokenKind_pp_directive_line, TokenKind_pp_directive_non_directive, TokenKind_pp_directive_nop, diff --git a/src/tokenize.c b/src/tokenize.c index 26c6e13..ceba12d 100644 --- a/src/tokenize.c +++ b/src/tokenize.c @@ -37,7 +37,7 @@ static void pplexer_tokenize_pp_directive(Lexer* l, Token* tok) { StrBuilder builder; strbuilder_init(&builder); - while (isalnum(infile_peek_char(l->src))) { + while (isalnum(infile_peek_char(l->src)) || infile_peek_char(l->src) == '_') { strbuilder_append_char(&builder, infile_peek_char(l->src)); infile_next_char(l->src); } @@ -70,6 +70,9 @@ static void pplexer_tokenize_pp_directive(Lexer* l, Token* tok) { } else if (strcmp(pp_directive_name, "include") == 0) { l->expect_header_name = true; tok->kind = TokenKind_pp_directive_include; + } else if (strcmp(pp_directive_name, "include_next") == 0) { + l->expect_header_name = true; + tok->kind = TokenKind_pp_directive_include_next; } else if (strcmp(pp_directive_name, "line") == 0) { tok->kind = TokenKind_pp_directive_line; } else if (strcmp(pp_directive_name, "pragma") == 0) { |
