aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authornsfisis <nsfisis@gmail.com>2026-01-14 23:10:40 +0900
committernsfisis <nsfisis@gmail.com>2026-01-17 00:27:28 +0900
commit82df1b08c42d40c79b0dbd1634e18786cad860da (patch)
treea2d2d9ab6aa9a03aa4e49b750fc597c0b3659c1c
parentfda0d40f4d353ce91592b9a1526cb463a279b6f8 (diff)
downloadducc-82df1b08c42d40c79b0dbd1634e18786cad860da.tar.gz
ducc-82df1b08c42d40c79b0dbd1634e18786cad860da.tar.zst
ducc-82df1b08c42d40c79b0dbd1634e18786cad860da.zip
feat: support empty arguments of ## operator
-rw-r--r--src/preprocess.c63
-rw-r--r--src/token.c2
-rw-r--r--src/token.h1
-rw-r--r--tests/macro_operators.sh15
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