KCC - Kayte C Compiler 1.10.0
A C compiler implementation with preprocessor, lexer, parser, and code generator
Loading...
Searching...
No Matches
preprocessor.c
1#include <stdio.h>
2#include <stdlib.h>
3#include <string.h>
4#include <stdbool.h>
5#include <stdarg.h>
6#include <ctype.h>
7#include <time.h>
8
9#include "preprocessor.h"
10#include "kcc.h"
11
12Preprocessor *preprocessor_create(void) {
13 Preprocessor *pp = malloc(sizeof(Preprocessor));
14 if (!pp) {
15 error_fatal("Memory allocation failed for preprocessor");
16 return NULL;
17 }
18
19 memset(pp, 0, sizeof(Preprocessor));
20
21 pp->output_capacity = 4096;
22 pp->output = malloc(pp->output_capacity);
23 if (!pp->output) {
24 free(pp);
25 error_fatal("Memory allocation failed for preprocessor output");
26 return NULL;
27 }
28
29 pp->output[0] = '\0';
30 pp->output_size = 0;
31 pp->current_line = 1;
32 pp->skip_lines = false;
33
34 // Add predefined macros
35 preprocessor_add_predefined_macros(pp);
36
37 return pp;
38}
39
40void preprocessor_destroy(Preprocessor *pp) {
41 if (!pp) return;
42
43 // Free include stack
44 for (int i = 0; i < pp->include_depth; i++) {
45 free(pp->include_stack[i].filename);
46 free(pp->include_stack[i].content);
47 }
48
49 // Free macro file_defined strings (if they exist in the struct)
50 // Note: Currently commented out due to missing field in header
51 // for (int i = 0; i < pp->macro_count; i++) {
52 // if (pp->macros[i].file_defined) {
53 // free(pp->macros[i].file_defined);
54 // }
55 // }
56
57 free(pp->output);
58 free(pp->current_file);
59 free(pp);
60}
61
62void preprocessor_add_predefined_macros(Preprocessor *pp) {
63 // Standard predefined macros
64 preprocessor_define_macro(pp, "__KCC__", "1");
65 preprocessor_define_macro(pp, "__VERSION__", "\"1.0.0\"");
66
67 // Date and time
68 time_t now = time(NULL);
69 struct tm *tm_info = localtime(&now);
70
71 char date_str[32];
72 char time_str[32];
73 strftime(date_str, sizeof(date_str), "\"%b %d %Y\"", tm_info);
74 strftime(time_str, sizeof(time_str), "\"%H:%M:%S\"", tm_info);
75
76 preprocessor_define_macro(pp, "__DATE__", date_str);
77 preprocessor_define_macro(pp, "__TIME__", time_str);
78
79 // Architecture and system
80 preprocessor_define_macro(pp, "__x86_64__", "1");
81 preprocessor_define_macro(pp, "__unix__", "1");
82
83 // C standard
84 preprocessor_define_macro(pp, "__STDC__", "1");
85 preprocessor_define_macro(pp, "__STDC_VERSION__", "201112L");
86
87 // Mark predefined macros
88 for (int i = 0; i < pp->macro_count; i++) {
89 pp->macros[i].is_predefined = true;
90 }
91}
92
93char* preprocessor_process_file(Preprocessor* pp, const char* filename) {
94 (void)pp; // Suppress unused parameter warning
95
96 char* content = read_file(filename);
97 if (!content) {
98 return NULL;
99 }
100
101 size_t content_len = strlen(content);
102 // Allocate extra space for potential additional newlines and safety margin
103 size_t result_capacity = content_len * 2 + 1024; // Double size plus safety margin
104 char* result = malloc(result_capacity);
105 if (!result) {
106 free(content);
107 return NULL;
108 }
109 result[0] = '\0';
110 size_t result_len = 0;
111
112 // Make a copy for strtok since it modifies the string
113 char* content_copy = strdup(content);
114 if (!content_copy) {
115 free(content);
116 free(result);
117 return NULL;
118 }
119
120 char* line = strtok(content_copy, "\n");
121 while (line != NULL) {
122 size_t line_len = strlen(line);
123
124 // Check if we have enough space (line + newline + null terminator)
125 if (result_len + line_len + 2 >= result_capacity) {
126 // Expand buffer
127 result_capacity *= 2;
128 char* new_result = realloc(result, result_capacity);
129 if (!new_result) {
130 free(content);
131 free(content_copy);
132 free(result);
133 return NULL;
134 }
135 result = new_result;
136 }
137
138 if (line[0] == '#') {
139 // Process directive - don't add to output if it's an include
140 if (strncmp(line + 1, "include", 7) == 0) {
141 // Skip include lines - don't add them to output
142 } else {
143 // Handle other directives or add them as-is
144 strcpy(result + result_len, line);
145 result_len += line_len;
146 result[result_len++] = '\n';
147 result[result_len] = '\0';
148 }
149 } else {
150 // Regular code line - add to output
151 strcpy(result + result_len, line);
152 result_len += line_len;
153 result[result_len++] = '\n';
154 result[result_len] = '\0';
155 }
156 line = strtok(NULL, "\n");
157 }
158
159 free(content);
160 free(content_copy);
161 return result;
162}
163
164char* preprocessor_process_string(Preprocessor* pp, const char* source, const char* filename) {
165 pp->current_file = strdup(filename);
166 pp->current_line = 1;
167 pp->output_size = 0;
168 pp->output[0] = '\0';
169
170 char line[MAX_LINE_LENGTH];
171 const char* src_ptr = source;
172
173 while (*src_ptr) {
174 // Extract line
175 int i = 0;
176 while (*src_ptr && *src_ptr != '\n' && i < MAX_LINE_LENGTH - 1) {
177 line[i++] = *src_ptr++;
178 }
179 line[i] = '\0';
180
181 if (*src_ptr == '\n') {
182 src_ptr++;
183 }
184
185 // Process line
186 if (preprocessor_should_skip_line(pp)) {
187 // Still need to process conditional directives when skipping
188 if (preprocessor_is_directive(line)) {
189 char *directive = preprocessor_get_directive_name(line);
190 if (directive && (strcmp(directive, "endif") == 0 ||
191 strcmp(directive, "else") == 0 ||
192 strcmp(directive, "elif") == 0)) {
193 preprocessor_process_directive(pp, line);
194 }
195 free(directive);
196 }
197 } else {
198 if (preprocessor_is_directive(line)) {
199 if (!preprocessor_process_directive(pp, line)) {
200 preprocessor_error(pp, "Error processing directive: %s", line);
201 }
202 } else {
203 // Expand macros and add to output
204 char *expanded = preprocessor_expand_macros(pp, line);
205 preprocessor_append_output(pp, expanded);
206 preprocessor_append_output(pp, "\n");
207 free(expanded);
208 }
209 }
210
211 pp->current_line++;
212 }
213
214 // Check for unmatched conditionals
215 if (pp->cond_stack_depth > 0) {
216 preprocessor_error(pp, "Unmatched conditional directive");
217 }
218
219 return strdup(pp->output);
220}
221
222bool preprocessor_define_macro(Preprocessor *pp, const char *name, const char *body) {
223 if (!pp || !name || !body) {
224 return false;
225 }
226
227 // Check for empty name
228 if (strlen(name) == 0) {
229 return false;
230 }
231
232 // Check if we have space for more macros
233 if (pp->macro_count >= MAX_MACROS) {
234 preprocessor_error(pp, "Too many macros defined");
235 return false;
236 }
237
238 // Get pointer to the macro structure in the array
239 void *macro_ptr = &pp->macros[pp->macro_count++];
240
241 // Access the macro struct directly - assuming standard macro structure
242 Macro *macro = (Macro *)macro_ptr;
243
244 // Use safe string copying with bounds checking
245 if (!safe_strcpy(macro->name, sizeof(macro->name), name)) {
246 pp->macro_count--; // Revert the increment
247 preprocessor_error(pp, "Macro name too long: %.50s", name);
248 return false;
249 }
250
251 // Check if body fits in fixed array
252 if (!safe_strcpy(macro->body, sizeof(macro->body), body)) {
253 pp->macro_count--; // Revert the increment
254 preprocessor_warning(pp, "Macro body truncated: %.50s", name);
255 // Continue anyway with truncated body
256 strncpy(macro->body, body, sizeof(macro->body) - 1);
257 macro->body[sizeof(macro->body) - 1] = '\0';
258 }
259
260 macro->type = MACRO_OBJECT;
261 macro->param_count = 0;
262 macro->is_predefined = false;
263 macro->line_defined = pp->current_line;
264 // Note: file_defined field removed due to missing field in header
265
266 return true;
267}
268
269bool preprocessor_define_function_macro(Preprocessor *pp, const char *name,
270 const char *params[], int param_count, const char *body) {
271 if (pp->macro_count >= MAX_MACROS) {
272 preprocessor_error(pp, "Too many macros defined");
273 return false;
274 }
275
276 if (param_count > MAX_MACRO_PARAMS) {
277 preprocessor_error(pp, "Too many macro parameters");
278 return false;
279 }
280
281 void *macro_ptr = &pp->macros[pp->macro_count++];
282
283 Macro *macro = (Macro *)macro_ptr;
284
285 strncpy(macro->name, name, sizeof(macro->name) - 1);
286 macro->name[sizeof(macro->name) - 1] = '\0';
287
288 strncpy(macro->body, body, sizeof(macro->body) - 1);
289 macro->body[sizeof(macro->body) - 1] = '\0';
290
291 macro->type = MACRO_FUNCTION;
292 macro->param_count = param_count;
293 macro->is_predefined = false;
294 macro->line_defined = pp->current_line;
295 // Note: file_defined field removed due to missing field in header
296
297 for (int i = 0; i < param_count; i++) {
298 strncpy(macro->params[i].name, params[i], sizeof(macro->params[i].name) - 1);
299 macro->params[i].name[sizeof(macro->params[i].name) - 1] = '\0';
300 }
301
302 return true;
303}
304
305bool preprocessor_undefine_macro(Preprocessor *pp, const char *name) {
306 for (int i = 0; i < pp->macro_count; i++) {
307 // Access macro fields through anonymous struct
308 void *macro_ptr = &pp->macros[i];
309 if (strcmp(((struct { char name[64]; char body[512]; int type; int param_count; bool is_predefined; int line_defined; char *file_defined; struct { char name[32]; } params[16]; } *)macro_ptr)->name, name) == 0) {
310 // Don't allow undefining predefined macros
311 if (((struct { char name[64]; char body[512]; int type; int param_count; bool is_predefined; int line_defined; char *file_defined; struct { char name[32]; } params[16]; } *)macro_ptr)->is_predefined) {
312 preprocessor_warning(pp, "Cannot undefine predefined macro '%s'", name);
313 return false;
314 }
315
316 // Free file_defined string
317 if (((struct { char name[64]; char body[512]; int type; int param_count; bool is_predefined; int line_defined; char *file_defined; struct { char name[32]; } params[16]; } *)macro_ptr)->file_defined) {
318 free(((struct { char name[64]; char body[512]; int type; int param_count; bool is_predefined; int line_defined; char *file_defined; struct { char name[32]; } params[16]; } *)macro_ptr)->file_defined);
319 }
320
321 // Shift remaining macros
322 for (int j = i; j < pp->macro_count - 1; j++) {
323 pp->macros[j] = pp->macros[j + 1];
324 }
325 pp->macro_count--;
326 return true;
327 }
328 }
329 return false;
330}
331
332Macro *preprocessor_find_macro(Preprocessor *pp, const char *name) {
333 for (int i = 0; i < pp->macro_count; i++) {
334 Macro *macro = &pp->macros[i];
335 if (strcmp(macro->name, name) == 0) {
336 return macro;
337 }
338 }
339 return NULL;
340}
341
342bool preprocessor_is_macro_defined(Preprocessor *pp, const char *name) {
343 return preprocessor_find_macro(pp, name) != NULL;
344}
345
346char *preprocessor_expand_macros(Preprocessor *pp, const char *line) {
347 (void)pp; // Suppress unused parameter warning
348 return strdup(line); // Simplified for now - just return a copy
349}
350
351char *preprocessor_expand_function_macro(Preprocessor *pp, Macro *macro,
352 const char *args[], int arg_count) {
353 (void)args; // Suppress unused parameter warning
354
355 void *macro_ptr = (void *)macro;
356 if (arg_count != ((struct { char name[64]; char body[512]; int type; int param_count; bool is_predefined; int line_defined; char *file_defined; struct { char name[32]; } params[16]; } *)macro_ptr)->param_count) {
357 preprocessor_error(pp, "Macro '%s' expects %d arguments, got %d",
358 ((struct { char name[64]; char body[512]; int type; int param_count; bool is_predefined; int line_defined; char *file_defined; struct { char name[32]; } params[16]; } *)macro_ptr)->name, ((struct { char name[64]; char body[512]; int type; int param_count; bool is_predefined; int line_defined; char *file_defined; struct { char name[32]; } params[16]; } *)macro_ptr)->param_count, arg_count);
359 return strdup(((struct { char name[64]; char body[512]; int type; int param_count; bool is_predefined; int line_defined; char *file_defined; struct { char name[32]; } params[16]; } *)macro_ptr)->body);
360 }
361
362 return strdup(((struct { char name[64]; char body[512]; int type; int param_count; bool is_predefined; int line_defined; char *file_defined; struct { char name[32]; } params[16]; } *)macro_ptr)->body); // Simplified for now
363}
364
365char *preprocessor_substitute_params(const char *body, const char *params[],
366 const char *args[], int param_count) {
367 (void)params; // Suppress unused parameter warning
368 (void)args; // Suppress unused parameter warning
369 (void)param_count; // Suppress unused parameter warning
370 return strdup(body); // Simplified for now
371}
372
373bool preprocessor_process_directive(Preprocessor *pp, const char *line) {
374 char *directive = preprocessor_get_directive_name(line);
375 if (!directive) return false;
376
377 bool result = false;
378
379 if (strcmp(directive, "define") == 0) {
380 result = preprocessor_handle_define(pp, line);
381 } else if (strcmp(directive, "undef") == 0) {
382 result = preprocessor_handle_undef(pp, line);
383 } else if (strcmp(directive, "include") == 0) {
384 result = preprocessor_handle_include(pp, line);
385 } else {
386 preprocessor_error(pp, "Unknown directive: #%s", directive);
387 }
388
389 free(directive);
390 return result;
391}
392
393bool preprocessor_handle_define(Preprocessor *pp, const char *directive) {
394 char *args = preprocessor_get_directive_args(directive);
395 if (!args) {
396 preprocessor_error(pp, "Invalid #define directive");
397 return false;
398 }
399
400 char *name = strtok(args, " \t");
401 if (!name) {
402 free(args);
403 preprocessor_error(pp, "Missing macro name in #define");
404 return false;
405 }
406
407 // Object-like macro
408 char *body = strtok(NULL, ""); // Get rest of line
409 if (!body) body = "";
410
411 // Skip leading whitespace in body
412 while (*body && isspace(*body)) body++;
413
414 bool result = preprocessor_define_macro(pp, name, body);
415 free(args);
416 return result;
417}
418
419bool preprocessor_handle_undef(Preprocessor *pp, const char *directive) {
420 char *args = preprocessor_get_directive_args(directive);
421 if (!args) {
422 preprocessor_error(pp, "Invalid #undef directive");
423 return false;
424 }
425
426 char *name = strtok(args, " \t\n");
427 if (!name) {
428 free(args);
429 preprocessor_error(pp, "Missing macro name in #undef");
430 return false;
431 }
432
433 bool result = preprocessor_undefine_macro(pp, name);
434 free(args);
435 return result;
436}
437
438bool preprocessor_handle_include(Preprocessor *pp, const char *directive) {
439 (void)pp; // Suppress unused parameter warning
440 (void)directive; // Suppress unused parameter warning
441 // Simplified - just return true for now
442 return true;
443}
444
445bool preprocessor_is_directive(const char *line) {
446 const char *ptr = line;
447 while (*ptr && isspace(*ptr)) ptr++;
448 return *ptr == '#';
449}
450
451char *preprocessor_get_directive_name(const char *line) {
452 const char *ptr = line;
453 while (*ptr && isspace(*ptr)) ptr++;
454 if (*ptr != '#') return NULL;
455
456 ptr++; // skip '#'
457 while (*ptr && isspace(*ptr)) ptr++;
458
459 const char *start = ptr;
460 while (*ptr && (isalnum(*ptr) || *ptr == '_')) ptr++;
461
462 if (ptr == start) return NULL;
463
464 char *name = malloc(ptr - start + 1);
465 strncpy(name, start, ptr - start);
466 name[ptr - start] = '\0';
467
468 return name;
469}
470
471char *preprocessor_get_directive_args(const char *line) {
472 const char *ptr = line;
473 while (*ptr && isspace(*ptr)) ptr++;
474 if (*ptr != '#') return NULL;
475
476 ptr++; // skip '#'
477 while (*ptr && isspace(*ptr)) ptr++;
478
479 // Skip directive name
480 while (*ptr && (isalnum(*ptr) || *ptr == '_')) ptr++;
481 while (*ptr && isspace(*ptr)) ptr++;
482
483 return strdup(ptr);
484}
485
486char *preprocessor_trim_whitespace(char *str) {
487 // Trim leading whitespace
488 while (*str && isspace(*str)) str++;
489
490 // Trim trailing whitespace
491 char *end = str + strlen(str) - 1;
492 while (end > str && isspace(*end)) {
493 *end = '\0';
494 end--;
495 }
496
497 return str;
498}
499
500char *preprocessor_stringify(const char *text) {
501 size_t len = strlen(text);
502 char *result = malloc(len * 2 + 3); // Worst case: every char needs escaping + quotes
503 char *ptr = result;
504
505 *ptr++ = '"';
506
507 for (size_t i = 0; i < len; i++) {
508 if (text[i] == '"' || text[i] == '\\') {
509 *ptr++ = '\\';
510 }
511 *ptr++ = text[i];
512 }
513
514 *ptr++ = '"';
515 *ptr = '\0';
516
517 return result;
518}
519
520void preprocessor_append_output(Preprocessor *pp, const char *text) {
521 size_t text_len = strlen(text);
522 size_t needed = pp->output_size + text_len + 1;
523
524 if (needed > pp->output_capacity) {
525 pp->output_capacity = needed * 2;
526 pp->output = realloc(pp->output, pp->output_capacity);
527 if (!pp->output) {
528 error_fatal("Memory allocation failed for preprocessor output");
529 return;
530 }
531 }
532
533 strcpy(pp->output + pp->output_size, text);
534 pp->output_size += text_len;
535}
536
537bool preprocessor_should_skip_line(Preprocessor *pp) {
538 return pp->skip_lines;
539}
540
541void preprocessor_push_conditional(Preprocessor *pp, ConditionalType type, bool condition) {
542 if (pp->cond_stack_depth >= 32) {
543 preprocessor_error(pp, "Conditional nesting too deep");
544 return;
545 }
546
547 ConditionalState *cond = &pp->cond_stack[pp->cond_stack_depth++];
548 cond->type = type;
549 cond->condition_met = condition;
550 cond->else_taken = false;
551 cond->line_number = pp->current_line;
552
553 pp->skip_lines = !condition;
554}
555
556bool preprocessor_pop_conditional(Preprocessor *pp) {
557 if (pp->cond_stack_depth == 0) {
558 return false;
559 }
560
561 pp->cond_stack_depth--;
562 pp->skip_lines = preprocessor_should_skip_line(pp);
563
564 return true;
565}
566
567int preprocessor_error(Preprocessor* pp, const char* format, ...) {
568 fprintf(stderr, "Preprocessor error in %s:%d: ",
569 pp->current_file ? pp->current_file : "unknown", pp->current_line);
570
571 va_list args;
572 va_start(args, format);
573 vfprintf(stderr, format, args);
574 va_end(args);
575
576 fprintf(stderr, "\n");
577
578 return 0;
579}
580
581int preprocessor_warning(Preprocessor* pp, const char* format, ...) {
582 fprintf(stderr, "Preprocessor warning in %s:%d: ",
583 pp->current_file ? pp->current_file : "unknown", pp->current_line);
584
585 va_list args;
586 va_start(args, format);
587 vfprintf(stderr, format, args);
588 va_end(args);
589
590 fprintf(stderr, "\n");
591
592 return 0;
593}
Conditional state tracking.
Definition types.h:598
Macro definition.
Definition types.h:572
ConditionalType
Conditional compilation state.
Definition types.h:586