diff options
| author | nsfisis <nsfisis@gmail.com> | 2025-08-30 01:13:30 +0900 |
|---|---|---|
| committer | nsfisis <nsfisis@gmail.com> | 2025-08-30 01:13:30 +0900 |
| commit | 941691595382f55c588a2d8fea79142be5146791 (patch) | |
| tree | e3b17487e72ad88719bbacbcfca64e56da9974a4 | |
| parent | 194e87a88290a4b3025436709e1f3064aecd00cd (diff) | |
| download | ducc-941691595382f55c588a2d8fea79142be5146791.tar.gz ducc-941691595382f55c588a2d8fea79142be5146791.tar.zst ducc-941691595382f55c588a2d8fea79142be5146791.zip | |
feat: implement ## operator
| -rw-r--r-- | src/preprocess.c | 73 | ||||
| -rw-r--r-- | src/std.h | 1 | ||||
| -rw-r--r-- | tests/107.sh | 42 |
3 files changed, 115 insertions, 1 deletions
diff --git a/src/preprocess.c b/src/preprocess.c index e47a390..b94f004 100644 --- a/src/preprocess.c +++ b/src/preprocess.c @@ -770,6 +770,48 @@ static MacroArgArray* pp_parse_macro_arguments(Preprocessor* pp) { return args; } +static Token* concat_two_tokens(Token* left, Token* right) { + StrBuilder builder; + strbuilder_init(&builder); + + // Left + if (left->kind == TokenKind_ident) { + strbuilder_append_string(&builder, left->value.string); + } else if (left->kind == TokenKind_literal_int) { + char buf[32]; + sprintf(buf, "%d", left->value.integer); + strbuilder_append_string(&builder, buf); + } else { + strbuilder_append_string(&builder, token_stringify(left)); + } + + // Right + if (right->kind == TokenKind_ident) { + strbuilder_append_string(&builder, right->value.string); + } else if (right->kind == TokenKind_literal_int) { + char buf[32]; + sprintf(buf, "%d", right->value.integer); + strbuilder_append_string(&builder, buf); + } else { + strbuilder_append_string(&builder, token_stringify(right)); + } + + // Concat + Token* result = calloc(1, sizeof(Token)); + + char* endptr; + int val = strtol(builder.buf, &endptr, 10); + if (*endptr == '\0') { + result->kind = TokenKind_literal_int; + result->value.integer = val; + } else { + result->kind = TokenKind_ident; + result->value.string = builder.buf; + } + + return result; +} + static BOOL expand_macro(Preprocessor* pp) { int macro_name_pos = pp->pos; Token* macro_name = next_pp_token(pp); @@ -783,6 +825,8 @@ static BOOL expand_macro(Preprocessor* pp) { if (macro->kind == MacroKind_func) { MacroArgArray* args = pp_parse_macro_arguments(pp); replace_pp_tokens(pp, macro_name_pos, pp->pos, ¯o->replacements); + + // Parameter substitution for (size_t i = 0; i < macro->replacements.len; ++i) { Token* tok = pp_token_at(pp, macro_name_pos + i); int macro_param_idx = macro_find_param(macro, tok); @@ -790,8 +834,35 @@ static BOOL expand_macro(Preprocessor* pp) { replace_pp_tokens(pp, macro_name_pos + i, macro_name_pos + i + 1, &args->data[macro_param_idx].tokens); } } + + // Handle ## operator + size_t result_len = 0; + for (int i = macro_name_pos; i < pp->pos && pp_token_at(pp, i)->kind != TokenKind_newline; ++i) { + Token* tok = pp_token_at(pp, i); + if (tok->kind == TokenKind_hashhash) { + // Concatenate previous and next tokens + if (result_len > 0 && i + 1 < pp->pos) { + Token* left = pp_token_at(pp, i - 1); + Token* right = pp_token_at(pp, i + 1); + Token* concatenated = concat_two_tokens(left, right); + + // Replace the three tokens (left ## right) with the concatenated one + TokenArray single_token; + tokens_init(&single_token, 1); + *tokens_push_new(&single_token) = *concatenated; + int new_pos = replace_pp_tokens(pp, i - 1, i + 2, &single_token); + i = new_pos - 1; + ++result_len; + } else { + fatal_error("invalid usage of ## operator"); + } + } else { + ++result_len; + } + } + // Inherit a source location from the original macro token. - for (size_t i = 0; i < macro->replacements.len; ++i) { + for (size_t i = 0; i < result_len; ++i) { pp_token_at(pp, macro_name_pos + i)->loc = original_loc; } } else if (macro->kind == MacroKind_obj) { @@ -34,6 +34,7 @@ int strncmp(const char*, const char*, size_t); char* strdup(const char*); char* strndup(const char*, size_t); char* strstr(const char*, const char*); +long strtol(const char*, char**, int); int system(const char*); #define assert(x) \ diff --git a/tests/107.sh b/tests/107.sh new file mode 100644 index 0000000..bb0c1a2 --- /dev/null +++ b/tests/107.sh @@ -0,0 +1,42 @@ +cat <<'EOF' > expected +foobar=100 +prefix_test=200 +test_suffix=300 +var_1=10 +var_2=20 +var_A=30 +number_12=12 +EOF + +test_diff <<'EOF' +int printf(); + +#define CONCAT(a, b) a##b +#define PREFIX(name) prefix_##name +#define SUFFIX(name) name##_suffix + +int CONCAT(foo, bar) = 100; +int PREFIX(test) = 200; +int SUFFIX(test) = 300; + +#define MAKE_VAR(n) var_##n +int MAKE_VAR(1) = 10; +int MAKE_VAR(2) = 20; + +#define A 0 +int MAKE_VAR(A) = 30; + +#define NUMBER(x, y) number_##x##y +int NUMBER(1, 2) = 12; + +int main() { + printf("foobar=%d\n", foobar); + printf("prefix_test=%d\n", prefix_test); + printf("test_suffix=%d\n", test_suffix); + printf("var_1=%d\n", var_1); + printf("var_2=%d\n", var_2); + printf("var_A=%d\n", var_A); + printf("number_12=%d\n", number_12); + return 0; +} +EOF |
