aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authornsfisis <nsfisis@gmail.com>2025-08-22 00:06:14 +0900
committernsfisis <nsfisis@gmail.com>2025-08-22 21:57:20 +0900
commit0ac6ac95283735dd70ebf55b26ef78a4c32c31de (patch)
treef4aa9c5c855fb31db29b95530454b994b6a78c2c
parenta2da09917886860ed478f4b37c31543e869cbd64 (diff)
downloadducc-0ac6ac95283735dd70ebf55b26ef78a4c32c31de.tar.gz
ducc-0ac6ac95283735dd70ebf55b26ef78a4c32c31de.tar.zst
ducc-0ac6ac95283735dd70ebf55b26ef78a4c32c31de.zip
feat: partially support #if directive
-rw-r--r--parse.c38
-rw-r--r--preprocess.c97
-rw-r--r--tests/097.sh20
3 files changed, 154 insertions, 1 deletions
diff --git a/parse.c b/parse.c
index 61f701e..8f06a83 100644
--- a/parse.c
+++ b/parse.c
@@ -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