aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authornsfisis <nsfisis@gmail.com>2025-07-20 01:07:40 +0900
committernsfisis <nsfisis@gmail.com>2025-08-15 10:04:26 +0900
commit274e212c16bb354532e4a955e594a159cef61665 (patch)
tree04acf78078538cd17bd698d8eaf4463d7c79bf99
parent82c99e92032b8101443671604de7137ba7257cd0 (diff)
downloadducc-274e212c16bb354532e4a955e594a159cef61665.tar.gz
ducc-274e212c16bb354532e4a955e594a159cef61665.tar.zst
ducc-274e212c16bb354532e4a955e594a159cef61665.zip
feat: partially implement #include directive
-rw-r--r--main.c80
-rw-r--r--tests/055.sh22
-rw-r--r--tests/056.sh30
-rw-r--r--tests/057.sh13
-rw-r--r--tests/058.sh25
5 files changed, 161 insertions, 9 deletions
diff --git a/main.c b/main.c
index 6b0ca3d..f5ef0aa 100644
--- a/main.c
+++ b/main.c
@@ -20,6 +20,7 @@ int isalpha(int);
int isdigit(int);
int isspace(int);
void* memcpy(void*, void*, size_t);
+void* memmove(void*, void*, size_t);
int printf(const char*, ...);
int sprintf(char*, const char*, ...);
int strcmp(const char*, const char*);
@@ -104,14 +105,23 @@ struct Preprocessor {
int n_pp_tokens;
PpDefine* pp_defines;
int n_pp_defines;
+ int include_depth;
};
typedef struct Preprocessor Preprocessor;
-Preprocessor* preprocessor_new(char* src) {
+PpToken* do_preprocess(char* src, int depth);
+
+Preprocessor* preprocessor_new(char* src, int include_depth) {
+ if (include_depth >= 32) {
+ fatal_error("include depth limit exceeded");
+ }
+
Preprocessor* pp = calloc(1, sizeof(Preprocessor));
pp->src = src;
pp->pp_tokens = calloc(1024 * 1024, sizeof(PpToken));
pp->pp_defines = calloc(1024, sizeof(PpDefine));
+ pp->include_depth = include_depth;
+
return pp;
}
@@ -403,12 +413,60 @@ void process_pp_directives(Preprocessor* pp) {
++pp->n_pp_defines;
}
}
- }
- while (tok != tok2 + 1) {
- tok->kind = PpTokenKind_whitespace;
- tok->raw.len = 0;
- tok->raw.data = NULL;
- ++tok;
+ // Remove #define directive.
+ while (tok != tok2 + 1) {
+ tok->kind = PpTokenKind_whitespace;
+ tok->raw.len = 0;
+ tok->raw.data = NULL;
+ ++tok;
+ }
+ } else if (tok2->kind == PpTokenKind_identifier && string_equals_cstr(&tok2->raw, "include")) {
+ ++tok2;
+ while (tok2->kind != PpTokenKind_eof && tok2->kind == PpTokenKind_whitespace)
+ ++tok2;
+ if (tok2->kind == PpTokenKind_string_literal) {
+ // Process #include directive.
+ PpToken* include_name = tok2;
+ ++tok2;
+ char* include_name_buf = calloc(include_name->raw.len - 2 + 1, sizeof(char));
+ sprintf(include_name_buf, "%.*s", include_name->raw.len - 2, include_name->raw.data + 1);
+
+ // Remove #include directive itself.
+ while (tok != tok2 + 1) {
+ tok->kind = PpTokenKind_whitespace;
+ tok->raw.len = 0;
+ tok->raw.data = NULL;
+ ++tok;
+ }
+
+ // Read and preprocess included file.
+ FILE* include_file = fopen(include_name_buf, "rb");
+ if (include_file == NULL) {
+ char* buf = calloc(1024, sizeof(char));
+ sprintf(buf, "cannot open include file: %s", include_name_buf);
+ fatal_error(buf);
+ }
+ char* include_source = read_all(include_file);
+ fclose(include_file);
+
+ PpToken* include_pp_tokens = do_preprocess(include_source, pp->include_depth + 1);
+
+ // Insert preprocessed tokens into the current token stream.
+ int n_include_pp_tokens = 0;
+ while (include_pp_tokens[n_include_pp_tokens].kind != PpTokenKind_eof) {
+ ++n_include_pp_tokens;
+ }
+
+ // Shift existing tokens to make room for include tokens
+ int n_pp_tokens_after_include = pp->n_pp_tokens - (tok - pp->pp_tokens);
+
+ memmove(tok + n_include_pp_tokens, tok, n_pp_tokens_after_include * sizeof(PpToken));
+
+ // Copy include tokens into the current position
+ memcpy(tok, include_pp_tokens, n_include_pp_tokens * sizeof(PpToken));
+
+ pp->n_pp_tokens += n_include_pp_tokens;
+ }
}
} else if (tok->kind == PpTokenKind_identifier) {
int pp_define_idx = find_pp_define(pp, &tok->raw);
@@ -423,13 +481,17 @@ void process_pp_directives(Preprocessor* pp) {
}
}
-PpToken* preprocess(char* src) {
- Preprocessor* pp = preprocessor_new(src);
+PpToken* do_preprocess(char* src, int depth) {
+ Preprocessor* pp = preprocessor_new(src, depth);
pp_tokenize_all(pp);
process_pp_directives(pp);
return pp->pp_tokens;
}
+PpToken* preprocess(char* src) {
+ return do_preprocess(src, 0);
+}
+
enum TokenKind {
TokenKind_eof,
diff --git a/tests/055.sh b/tests/055.sh
new file mode 100644
index 0000000..d5e65f3
--- /dev/null
+++ b/tests/055.sh
@@ -0,0 +1,22 @@
+set -e
+
+cat <<'EOF' > expected
+8
+EOF
+
+cat <<'EOF' > header.h
+int add(int a, int b) {
+ return a + b;
+}
+
+int printf(const char*, ...);
+EOF
+
+bash ../../test_diff.sh <<'EOF'
+#include "header.h"
+
+int main() {
+ printf("%d\n", add(5, 3));
+ return 0;
+}
+EOF
diff --git a/tests/056.sh b/tests/056.sh
new file mode 100644
index 0000000..7bad4c0
--- /dev/null
+++ b/tests/056.sh
@@ -0,0 +1,30 @@
+set -e
+
+cat <<'EOF' > expected
+12
+EOF
+
+cat <<'EOF' > math.h
+int multiply(int a, int b) {
+ return a * b;
+}
+EOF
+
+cat <<'EOF' > calc.h
+#include "math.h"
+
+int calculate(int x) {
+ return multiply(x, 2);
+}
+
+int printf(const char*, ...);
+EOF
+
+bash ../../test_diff.sh <<'EOF'
+#include "calc.h"
+
+int main() {
+ printf("%d\n", calculate(6));
+ return 0;
+}
+EOF
diff --git a/tests/057.sh b/tests/057.sh
new file mode 100644
index 0000000..6d1265f
--- /dev/null
+++ b/tests/057.sh
@@ -0,0 +1,13 @@
+set -e
+
+cat <<'EOF' > expected
+cannot open include file: nonexistent.h
+EOF
+
+bash ../../test_compile_error.sh <<'EOF'
+#include "nonexistent.h"
+
+int main() {
+ return 0;
+}
+EOF
diff --git a/tests/058.sh b/tests/058.sh
new file mode 100644
index 0000000..3be7ad1
--- /dev/null
+++ b/tests/058.sh
@@ -0,0 +1,25 @@
+set -e
+
+cat <<'EOF' > expected
+include depth limit exceeded
+EOF
+
+# Create circular include files
+cat <<'EOF' > a.h
+#include "b.h"
+int a() { return 1; }
+EOF
+
+cat <<'EOF' > b.h
+#include "a.h"
+int b() { return 2; }
+EOF
+
+bash ../../test_compile_error.sh <<'EOF'
+#include "a.h"
+
+int main() {
+ a() + b();
+ return 0;
+}
+EOF