diff options
| author | nsfisis <nsfisis@gmail.com> | 2025-08-22 00:06:14 +0900 |
|---|---|---|
| committer | nsfisis <nsfisis@gmail.com> | 2025-08-22 21:57:20 +0900 |
| commit | 0ac6ac95283735dd70ebf55b26ef78a4c32c31de (patch) | |
| tree | f4aa9c5c855fb31db29b95530454b994b6a78c2c | |
| parent | a2da09917886860ed478f4b37c31543e869cbd64 (diff) | |
| download | ducc-0ac6ac95283735dd70ebf55b26ef78a4c32c31de.tar.gz ducc-0ac6ac95283735dd70ebf55b26ef78a4c32c31de.tar.zst ducc-0ac6ac95283735dd70ebf55b26ef78a4c32c31de.zip | |
feat: partially support #if directive
| -rw-r--r-- | parse.c | 38 | ||||
| -rw-r--r-- | preprocess.c | 97 | ||||
| -rw-r--r-- | tests/097.sh | 20 |
3 files changed, 154 insertions, 1 deletions
@@ -697,6 +697,12 @@ AstNode* parse_conditional_expr(Parser* p) { } } +// constant-expression: +// conditional-expression +AstNode* parse_constant_expression(Parser* p) { + return parse_conditional_expr(p); +} + AstNode* parse_assignment_expr(Parser* p) { AstNode* lhs = parse_conditional_expr(p); while (1) { @@ -1364,3 +1370,35 @@ Program* parse(TokenArray* tokens) { prog->str_literals = p->str_literals; return prog; } + +int eval(AstNode* e) { + if (e->kind == AstNodeKind_int_expr) { + return e->node_int_value; + } else if (e->kind == AstNodeKind_unary_expr) { + int v = eval(e->node_operand); + if (e->node_op == TokenKind_not) { + return !v; + } else { + unimplemented(); + } + } else if (e->kind == AstNodeKind_binary_expr || e->kind == AstNodeKind_logical_expr) { + int v1 = eval(e->node_lhs); + int v2 = eval(e->node_rhs); + if (e->node_op == TokenKind_andand) { + return v1 && v2; + } else if (e->node_op == TokenKind_oror) { + return v1 || v2; + } else { + unimplemented(); + } + } else { + unimplemented(); + } +} + +BOOL pp_eval_constant_expression(TokenArray* pp_tokens) { + TokenArray* tokens = tokenize(pp_tokens); + Parser* p = parser_new(tokens); + AstNode* e = parse_constant_expression(p); + return eval(e) != 0; +} diff --git a/preprocess.c b/preprocess.c index 314bd1d..b1810cd 100644 --- a/preprocess.c +++ b/preprocess.c @@ -25,6 +25,10 @@ enum TokenKind { TokenKind_pp_directive_pragma, TokenKind_pp_directive_undef, TokenKind_pp_directive_warning, + TokenKind_pp_operator_defined, + TokenKind_pp_operator___has_c_attribute, + TokenKind_pp_operator___has_embed, + TokenKind_pp_operator___has_include, // C23: 6.4.1 TokenKind_keyword_alignas, @@ -183,6 +187,14 @@ const char* token_kind_stringify(TokenKind k) { return "#undef"; else if (k == TokenKind_pp_directive_warning) return "#warning"; + else if (k == TokenKind_pp_operator_defined) + return "defined"; + else if (k == TokenKind_pp_operator___has_c_attribute) + return "__has_c_attribute"; + else if (k == TokenKind_pp_operator___has_embed) + return "__has_embed"; + else if (k == TokenKind_pp_operator___has_include) + return "__has_include"; else if (k == TokenKind_keyword_alignas) return "alignas"; else if (k == TokenKind_keyword_alignof) @@ -1093,8 +1105,91 @@ void process_elif_directive(Preprocessor* pp, int directive_token_pos) { unimplemented(); } +BOOL pp_eval_constant_expression(TokenArray*); +int replace_pp_tokens(Preprocessor*, int, int, TokenArray*); +BOOL expand_macro(Preprocessor*); + void process_if_directive(Preprocessor* pp, int directive_token_pos) { - unimplemented(); + next_pp_token(pp); + int condition_expression_start_pos = pp->pos; + + while (!pp_eof(pp)) { + Token* tok = peek_pp_token(pp); + if (tok->kind == TokenKind_newline) { + break; + } else if (tok->kind == TokenKind_ident) { + if (strcmp(tok->value.string, "defined") == 0) { + int defined_pos = pp->pos; + // 'defined' <ws>* '(' <ws>* <ident> <ws>* ')' + // 'defined' <ws>* <ident> + next_pp_token(pp); + skip_whitespaces(pp); + Token* macro_name; + if (peek_pp_token(pp)->kind == TokenKind_paren_l) { + next_pp_token(pp); + skip_whitespaces(pp); + macro_name = next_pp_token(pp); + if (macro_name->kind != TokenKind_ident) { + fatal_error("invalid defined"); + } + skip_whitespaces(pp); + if (next_pp_token(pp)->kind != TokenKind_paren_r) { + fatal_error("invalid defined"); + } + } else { + macro_name = next_pp_token(pp); + if (macro_name->kind != TokenKind_ident) { + fatal_error("invalid defined"); + } + } + BOOL is_defined = find_macro(pp, macro_name->value.string) != -1; + TokenArray defined_results; + tokens_init(&defined_results, 1); + Token* defined_result = tokens_push_new(&defined_results); + defined_result->kind = TokenKind_literal_int; + defined_result->value.integer = is_defined; + pp->pos = replace_pp_tokens(pp, defined_pos, pp->pos, &defined_results); + } else { + BOOL expanded = expand_macro(pp); + if (expanded) { + // A macro may expand to another macro. Re-scan the expanded tokens. + // TODO: if the macro is defined recursively, it causes infinite loop. + } else { + next_pp_token(pp); + } + } + } else { + next_pp_token(pp); + } + } + + // all remaining identifiers other than true (including those lexically identical to keywords such as false) are + // replaced with the pp-number 0, true is replaced with pp-number 1, and then each preprocessing token is converted + // into a token. + for (int pos = condition_expression_start_pos; pos < pp->pos; ++pos) { + Token* tok = pp_token_at(pp, pos); + if (tok->kind == TokenKind_ident) { + BOOL is_true = strcmp(tok->value.string, "true") == 0; + tok->kind = TokenKind_literal_int; + tok->value.integer = is_true; + } + } + + int condition_expression_tokens_len = pp->pos - condition_expression_start_pos; + TokenArray condition_expression_tokens; + // +1 to add EOF token at the end. + tokens_init(&condition_expression_tokens, condition_expression_tokens_len + 1); + for (int i = 0; i < condition_expression_tokens_len; ++i) { + *tokens_push_new(&condition_expression_tokens) = *pp_token_at(pp, condition_expression_start_pos + i); + } + Token* eof_tok = tokens_push_new(&condition_expression_tokens); + eof_tok->kind = TokenKind_eof; + + BOOL result = pp_eval_constant_expression(&condition_expression_tokens); + + pp->skip_pp_tokens = !result; + + remove_directive_tokens(pp, directive_token_pos, pp->pos); } void process_ifdef_directive(Preprocessor* pp, int directive_token_pos) { diff --git a/tests/097.sh b/tests/097.sh new file mode 100644 index 0000000..00d67f2 --- /dev/null +++ b/tests/097.sh @@ -0,0 +1,20 @@ +set -e + +cat <<'EOF' > expected +1 +EOF + +bash ../../test_diff.sh <<'EOF' +int printf(); + +#define A +#define B + +int main() { +#if defined A && defined(B) + printf("1\n"); +#else + printf("2\n"); +#endif +} +EOF |
