diff options
| author | nsfisis <nsfisis@gmail.com> | 2026-01-14 23:10:40 +0900 |
|---|---|---|
| committer | nsfisis <nsfisis@gmail.com> | 2026-01-17 00:27:28 +0900 |
| commit | 82df1b08c42d40c79b0dbd1634e18786cad860da (patch) | |
| tree | a2d2d9ab6aa9a03aa4e49b750fc597c0b3659c1c | |
| parent | fda0d40f4d353ce91592b9a1526cb463a279b6f8 (diff) | |
| download | ducc-82df1b08c42d40c79b0dbd1634e18786cad860da.tar.gz ducc-82df1b08c42d40c79b0dbd1634e18786cad860da.tar.zst ducc-82df1b08c42d40c79b0dbd1634e18786cad860da.zip | |
feat: support empty arguments of ## operator
| -rw-r--r-- | src/preprocess.c | 63 | ||||
| -rw-r--r-- | src/token.c | 2 | ||||
| -rw-r--r-- | src/token.h | 1 | ||||
| -rw-r--r-- | tests/macro_operators.sh | 15 |
4 files changed, 70 insertions, 11 deletions
diff --git a/src/preprocess.c b/src/preprocess.c index 268147e..54e3648 100644 --- a/src/preprocess.c +++ b/src/preprocess.c @@ -726,10 +726,23 @@ static int expand_macro(Preprocessor* pp, bool skip_newline, MacroExpansionConte int macro_param_idx = macro_find_param(macro, tok); if (macro_param_idx != -1) { size_t arg_token_count = args->data[macro_param_idx].tokens.len; - replace_pp_tokens(pp, macro_name_pos + i + offset, macro_name_pos + i + offset + 1, - &args->data[macro_param_idx].tokens); - token_count += arg_token_count; - offset += arg_token_count - 1; + if (arg_token_count == 0) { + // Empty argument: insert a placemarker token + TokenArray placemarker_token; + tokens_init(&placemarker_token, 1); + Token* pm = tokens_push_new(&placemarker_token); + pm->kind = TokenKind_placemarker; + pm->loc = tok->loc; + replace_pp_tokens(pp, macro_name_pos + i + offset, macro_name_pos + i + offset + 1, + &placemarker_token); + token_count += 1; + // offset stays the same (1 - 1 = 0) + } else { + replace_pp_tokens(pp, macro_name_pos + i + offset, macro_name_pos + i + offset + 1, + &args->data[macro_param_idx].tokens); + token_count += arg_token_count; + offset += arg_token_count - 1; + } } else { ++token_count; } @@ -757,15 +770,35 @@ static int expand_macro(Preprocessor* pp, bool skip_newline, MacroExpansionConte } } if (lhs_pos == -1 || rhs_pos == -1) { - fatal_error("invalid usage of ## operator"); + fatal_error("%s:%d: invalid usage of ## operator", tok->loc.filename, tok->loc.line); + } + + Token* lhs_tok = pp_token_at(pp, lhs_pos); + Token* rhs_tok = pp_token_at(pp, rhs_pos); + bool lhs_is_placemarker = lhs_tok->kind == TokenKind_placemarker; + bool rhs_is_placemarker = rhs_tok->kind == TokenKind_placemarker; + + TokenArray result_tokens; + tokens_init(&result_tokens, 1); + + if (lhs_is_placemarker && rhs_is_placemarker) { + // Both are placemarkers: result is a placemarker + Token* pm = tokens_push_new(&result_tokens); + pm->kind = TokenKind_placemarker; + pm->loc = tok->loc; + } else if (lhs_is_placemarker) { + // Left is placemarker: result is the right token + *tokens_push_new(&result_tokens) = *rhs_tok; + } else if (rhs_is_placemarker) { + // Right is placemarker: result is the left token + *tokens_push_new(&result_tokens) = *lhs_tok; + } else { + // Neither is placemarker: concatenate them + Token* concatenated = concat_two_tokens(lhs_tok, rhs_tok); + *tokens_push_new(&result_tokens) = *concatenated; } - Token* concatenated = concat_two_tokens(pp_token_at(pp, lhs_pos), pp_token_at(pp, rhs_pos)); - // Replace the three tokens (lhs ## rhs) with the concatenated one - TokenArray single_token; - tokens_init(&single_token, 1); - *tokens_push_new(&single_token) = *concatenated; - replace_pp_tokens(pp, lhs_pos, rhs_pos + 1, &single_token); + replace_pp_tokens(pp, lhs_pos, rhs_pos + 1, &result_tokens); token_count -= rhs_pos - lhs_pos; i -= pos - lhs_pos; token_count2 -= pos - lhs_pos - 1; @@ -774,6 +807,14 @@ static int expand_macro(Preprocessor* pp, bool skip_newline, MacroExpansionConte } } + // Remove placemarker tokens after ## processing + for (size_t i = 0; i < token_count2; ++i) { + Token* tok = pp_token_at(pp, macro_name_pos + i); + if (tok->kind == TokenKind_placemarker) { + tok->kind = TokenKind_removed; + } + } + // Inherit a source location from the original macro token. for (size_t i = 0; i < token_count2; ++i) { pp_token_at(pp, macro_name_pos + i)->loc = original_loc; diff --git a/src/token.c b/src/token.c index 7e54729..ebb803e 100644 --- a/src/token.c +++ b/src/token.c @@ -13,6 +13,8 @@ const char* token_kind_stringify(TokenKind k) { return "<whitespace>"; else if (k == TokenKind_removed) return "<removed>"; + else if (k == TokenKind_placemarker) + return "<placemarker>"; else if (k == TokenKind_newline) return "<new-line>"; else if (k == TokenKind_other) diff --git a/src/token.h b/src/token.h index 9dc89e8..e093a8c 100644 --- a/src/token.h +++ b/src/token.h @@ -12,6 +12,7 @@ typedef enum { TokenKind_hashhash, TokenKind_whitespace, TokenKind_removed, + TokenKind_placemarker, TokenKind_newline, TokenKind_other, TokenKind_character_constant, diff --git a/tests/macro_operators.sh b/tests/macro_operators.sh index a6d0445..57e8564 100644 --- a/tests/macro_operators.sh +++ b/tests/macro_operators.sh @@ -107,3 +107,18 @@ int main() { ASSERT_EQ(0, strcmp("FOO", BAZ)); } EOF + +test_exit_code 0 <<'EOF' +#include <helpers.h> + +#define CONCAT3(x, y, z) x##y##z +#define FOO CONCAT3(, 2, 3) +#define BAR CONCAT3(1, , 3) +#define BAZ CONCAT3(1, 2, ) + +int main() { + ASSERT_EQ(23, FOO); + ASSERT_EQ(13, BAR); + ASSERT_EQ(12, BAZ); +} +EOF |
