Added include functionallity.
This commit is contained in:
parent
6c8a15272a
commit
0ec0590ad0
21
Makefile
21
Makefile
@ -16,7 +16,8 @@ SOURCES = $(SRC_DIR)/main.c \
|
||||
$(SRC_DIR)/parser.c \
|
||||
$(SRC_DIR)/interpreter.c \
|
||||
$(SRC_DIR)/native_functions.c \
|
||||
$(SRC_DIR)/string_utils.c
|
||||
$(SRC_DIR)/string_utils.c \
|
||||
$(SRC_DIR)/preprocessor.c
|
||||
|
||||
OBJECTS = $(SOURCES:$(SRC_DIR)/%.c=$(BUILD_DIR)/%.o)
|
||||
|
||||
@ -36,7 +37,9 @@ TESTS = $(TEST_DIR)/feature_test.rc \
|
||||
$(TEST_DIR)/comparison_test.rc \
|
||||
$(TEST_DIR)/functions_test.rc \
|
||||
$(TEST_DIR)/trig_test.rc \
|
||||
$(TEST_DIR)/file_seek_test.rc
|
||||
$(TEST_DIR)/file_seek_test.rc \
|
||||
$(TEST_DIR)/include_test.rc \
|
||||
$(TEST_DIR)/nested_include_test.rc
|
||||
|
||||
EXAMPLES = $(EXAMPLE_DIR)/http_simple.rc \
|
||||
$(EXAMPLE_DIR)/http_persistent.rc \
|
||||
@ -113,6 +116,12 @@ test: $(TARGET)
|
||||
@echo "Running file seek tests..."
|
||||
@$(TARGET) $(TEST_DIR)/file_seek_test.rc 2>&1 | grep -v "Error at token" || true
|
||||
@echo ""
|
||||
@echo "Running include tests..."
|
||||
@$(TARGET) $(TEST_DIR)/include_test.rc 2>&1 | grep -v "Error at token" || true
|
||||
@echo ""
|
||||
@echo "Running nested include tests..."
|
||||
@$(TARGET) $(TEST_DIR)/nested_include_test.rc 2>&1 | grep -v "Error at token" || true
|
||||
@echo ""
|
||||
@echo "=== All Tests Completed ==="
|
||||
|
||||
run-feature-test: $(TARGET)
|
||||
@ -166,6 +175,12 @@ run-trig-test: $(TARGET)
|
||||
run-file-seek-test: $(TARGET)
|
||||
$(TARGET) $(TEST_DIR)/file_seek_test.rc
|
||||
|
||||
run-include-test: $(TARGET)
|
||||
$(TARGET) $(TEST_DIR)/include_test.rc
|
||||
|
||||
run-nested-include-test: $(TARGET)
|
||||
$(TARGET) $(TEST_DIR)/nested_include_test.rc
|
||||
|
||||
run-http-simple: $(TARGET)
|
||||
$(TARGET) $(EXAMPLE_DIR)/http_simple.rc
|
||||
|
||||
@ -204,6 +219,8 @@ help:
|
||||
@echo " make run-functions-test - Run functions_test.rc (function calls & recursion)"
|
||||
@echo " make run-trig-test - Run trig_test.rc (sin, cos, tan)"
|
||||
@echo " make run-file-seek-test - Run file_seek_test.rc (fseek, ftell)"
|
||||
@echo " make run-include-test - Run include_test.rc (#include directive)"
|
||||
@echo " make run-nested-include-test - Run nested_include_test.rc (nested includes)"
|
||||
@echo ""
|
||||
@echo "Examples:"
|
||||
@echo " make run-http-simple - Run http_simple.rc (single connection HTTP server)"
|
||||
|
||||
26
src/main.c
26
src/main.c
@ -1,9 +1,12 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <libgen.h>
|
||||
#include <string.h>
|
||||
#include "types.h"
|
||||
#include "tokenizer.h"
|
||||
#include "interpreter.h"
|
||||
#include "native_functions.h"
|
||||
#include "preprocessor.h"
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
if (argc < 2) {
|
||||
@ -11,24 +14,21 @@ int main(int argc, char **argv) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
FILE *f = fopen(argv[1], "rb");
|
||||
if (!f) {
|
||||
printf("Could not open file.\n");
|
||||
return 1;
|
||||
}
|
||||
char file_path[512];
|
||||
strncpy(file_path, argv[1], 511);
|
||||
file_path[511] = 0;
|
||||
|
||||
src_code = malloc(MAX_SRC + 1);
|
||||
char dir_path[512];
|
||||
strncpy(dir_path, argv[1], 511);
|
||||
dir_path[511] = 0;
|
||||
char *dir = dirname(dir_path);
|
||||
|
||||
src_code = preprocess(file_path, dir);
|
||||
if (!src_code) {
|
||||
printf("Memory allocation failed.\n");
|
||||
fclose(f);
|
||||
printf("Preprocessing failed.\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
size_t n = fread(src_code, 1, MAX_SRC, f);
|
||||
if (n >= MAX_SRC) n = MAX_SRC - 1;
|
||||
src_code[n] = 0;
|
||||
fclose(f);
|
||||
|
||||
register_native_functions();
|
||||
|
||||
tokenize(src_code);
|
||||
|
||||
247
src/preprocessor.c
Normal file
247
src/preprocessor.c
Normal file
@ -0,0 +1,247 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <libgen.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include "preprocessor.h"
|
||||
|
||||
static IncludedFile included_files[MAX_INCLUDED_FILES];
|
||||
static int included_count = 0;
|
||||
static int include_depth = 0;
|
||||
|
||||
static void normalize_path(const char *path, char *normalized) {
|
||||
strncpy(normalized, path, 255);
|
||||
normalized[255] = 0;
|
||||
}
|
||||
|
||||
static int is_already_included(const char *path) {
|
||||
char normalized[256];
|
||||
normalize_path(path, normalized);
|
||||
|
||||
for (int i = 0; i < included_count; i++) {
|
||||
if (strcmp(included_files[i].path, normalized) == 0) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mark_as_included(const char *path) {
|
||||
if (included_count >= MAX_INCLUDED_FILES) {
|
||||
fprintf(stderr, "Error: Too many included files (max %d)\n", MAX_INCLUDED_FILES);
|
||||
return;
|
||||
}
|
||||
|
||||
char normalized[256];
|
||||
normalize_path(path, normalized);
|
||||
|
||||
strncpy(included_files[included_count].path, normalized, 255);
|
||||
included_files[included_count].path[255] = 0;
|
||||
included_files[included_count].included = 1;
|
||||
included_count++;
|
||||
}
|
||||
|
||||
static char* read_file(const char *filename) {
|
||||
FILE *f = fopen(filename, "rb");
|
||||
if (!f) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
fseek(f, 0, SEEK_END);
|
||||
long size = ftell(f);
|
||||
fseek(f, 0, SEEK_SET);
|
||||
|
||||
if (size <= 0 || size > MAX_PREPROCESSED_SIZE) {
|
||||
fclose(f);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char *content = malloc(size + 1);
|
||||
if (!content) {
|
||||
fclose(f);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
size_t read_size = fread(content, 1, size, f);
|
||||
content[read_size] = 0;
|
||||
fclose(f);
|
||||
|
||||
return content;
|
||||
}
|
||||
|
||||
static void resolve_include_path(const char *current_dir, const char *include_file, char *resolved_path) {
|
||||
if (include_file[0] == '/') {
|
||||
strncpy(resolved_path, include_file, 511);
|
||||
resolved_path[511] = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
snprintf(resolved_path, 512, "%s/%s", current_dir, include_file);
|
||||
}
|
||||
|
||||
static char* process_includes(const char *filename, const char *content, const char *current_dir);
|
||||
|
||||
static char* extract_include_filename(const char *line) {
|
||||
const char *p = line;
|
||||
while (*p && isspace(*p)) p++;
|
||||
|
||||
if (*p != '#') return NULL;
|
||||
p++;
|
||||
|
||||
while (*p && isspace(*p)) p++;
|
||||
|
||||
if (strncmp(p, "include", 7) != 0) return NULL;
|
||||
p += 7;
|
||||
|
||||
while (*p && isspace(*p)) p++;
|
||||
|
||||
if (*p != '"' && *p != '<') return NULL;
|
||||
|
||||
char quote = (*p == '"') ? '"' : '>';
|
||||
p++;
|
||||
|
||||
static char filename[256];
|
||||
int i = 0;
|
||||
while (*p && *p != quote && i < 255) {
|
||||
filename[i++] = *p++;
|
||||
}
|
||||
filename[i] = 0;
|
||||
|
||||
if (*p != quote) return NULL;
|
||||
|
||||
return filename;
|
||||
}
|
||||
|
||||
static char* process_includes(const char *filename, const char *content, const char *current_dir) {
|
||||
if (include_depth >= MAX_INCLUDE_DEPTH) {
|
||||
fprintf(stderr, "Error: Include depth exceeded (max %d) in file: %s\n",
|
||||
MAX_INCLUDE_DEPTH, filename);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
include_depth++;
|
||||
|
||||
size_t result_size = MAX_PREPROCESSED_SIZE;
|
||||
char *result = malloc(result_size);
|
||||
if (!result) {
|
||||
fprintf(stderr, "Error: Memory allocation failed\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
result[0] = 0;
|
||||
size_t result_len = 0;
|
||||
|
||||
const char *p = content;
|
||||
char line[1024];
|
||||
|
||||
while (*p) {
|
||||
int line_len = 0;
|
||||
while (*p && *p != '\n' && line_len < 1023) {
|
||||
line[line_len++] = *p++;
|
||||
}
|
||||
line[line_len] = 0;
|
||||
if (*p == '\n') {
|
||||
p++;
|
||||
}
|
||||
|
||||
char *include_file = extract_include_filename(line);
|
||||
|
||||
if (include_file) {
|
||||
char resolved_path[512];
|
||||
resolve_include_path(current_dir, include_file, resolved_path);
|
||||
|
||||
if (is_already_included(resolved_path)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
mark_as_included(resolved_path);
|
||||
|
||||
char *included_content = read_file(resolved_path);
|
||||
if (!included_content) {
|
||||
fprintf(stderr, "Error: Cannot open included file: %s\n", resolved_path);
|
||||
fprintf(stderr, " Included from: %s\n", filename);
|
||||
free(result);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
char include_dir[512];
|
||||
char temp_path[512];
|
||||
strncpy(temp_path, resolved_path, 511);
|
||||
temp_path[511] = 0;
|
||||
char *dir = dirname(temp_path);
|
||||
strncpy(include_dir, dir, 511);
|
||||
include_dir[511] = 0;
|
||||
|
||||
char *processed = process_includes(resolved_path, included_content, include_dir);
|
||||
free(included_content);
|
||||
|
||||
size_t processed_len = strlen(processed);
|
||||
if (result_len + processed_len + 2 >= result_size) {
|
||||
fprintf(stderr, "Error: Preprocessed output too large (max %d bytes)\n",
|
||||
MAX_PREPROCESSED_SIZE);
|
||||
free(processed);
|
||||
free(result);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
memcpy(result + result_len, processed, processed_len);
|
||||
result_len += processed_len;
|
||||
result[result_len++] = '\n';
|
||||
result[result_len] = 0;
|
||||
|
||||
free(processed);
|
||||
} else {
|
||||
size_t line_total = line_len + 1;
|
||||
if (result_len + line_total >= result_size) {
|
||||
fprintf(stderr, "Error: Preprocessed output too large (max %d bytes)\n",
|
||||
MAX_PREPROCESSED_SIZE);
|
||||
free(result);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (line_len > 0) {
|
||||
memcpy(result + result_len, line, line_len);
|
||||
result_len += line_len;
|
||||
}
|
||||
result[result_len++] = '\n';
|
||||
result[result_len] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
include_depth--;
|
||||
return result;
|
||||
}
|
||||
|
||||
char* preprocess(const char *filename, const char *source_dir) {
|
||||
included_count = 0;
|
||||
include_depth = 0;
|
||||
memset(included_files, 0, sizeof(included_files));
|
||||
|
||||
mark_as_included(filename);
|
||||
|
||||
char *content = read_file(filename);
|
||||
if (!content) {
|
||||
fprintf(stderr, "Error: Cannot read file: %s\n", filename);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char dir_path[512];
|
||||
if (source_dir) {
|
||||
strncpy(dir_path, source_dir, 511);
|
||||
dir_path[511] = 0;
|
||||
} else {
|
||||
char temp_path[512];
|
||||
strncpy(temp_path, filename, 511);
|
||||
temp_path[511] = 0;
|
||||
char *dir = dirname(temp_path);
|
||||
strncpy(dir_path, dir, 511);
|
||||
dir_path[511] = 0;
|
||||
}
|
||||
|
||||
char *result = process_includes(filename, content, dir_path);
|
||||
free(content);
|
||||
|
||||
return result;
|
||||
}
|
||||
15
src/preprocessor.h
Normal file
15
src/preprocessor.h
Normal file
@ -0,0 +1,15 @@
|
||||
#ifndef PREPROCESSOR_H
|
||||
#define PREPROCESSOR_H
|
||||
|
||||
#define MAX_INCLUDE_DEPTH 32
|
||||
#define MAX_INCLUDED_FILES 256
|
||||
#define MAX_PREPROCESSED_SIZE 500000
|
||||
|
||||
typedef struct {
|
||||
char path[256];
|
||||
int included;
|
||||
} IncludedFile;
|
||||
|
||||
char* preprocess(const char *filename, const char *source_dir);
|
||||
|
||||
#endif
|
||||
27
tests/include_test.rc
Normal file
27
tests/include_test.rc
Normal file
@ -0,0 +1,27 @@
|
||||
#include "includes/math_lib.rc"
|
||||
|
||||
int main() {
|
||||
printf("=== Include Directive Tests ===\n");
|
||||
|
||||
printf("Test 1: Basic include\n");
|
||||
int sum = add(10, 5);
|
||||
printf("add(10, 5) = %d\n", sum);
|
||||
printf("PASS: Included function works\n");
|
||||
|
||||
printf("Test 2: Multiple functions from include\n");
|
||||
int diff = subtract(20, 7);
|
||||
printf("subtract(20, 7) = %d\n", diff);
|
||||
int prod = multiply(6, 4);
|
||||
printf("multiply(6, 4) = %d\n", prod);
|
||||
int quot = divide(15, 3);
|
||||
printf("divide(15, 3) = %d\n", quot);
|
||||
printf("PASS: All included functions work\n");
|
||||
|
||||
printf("Test 3: Using included functions in expressions\n");
|
||||
int result = add(multiply(3, 4), subtract(10, 5));
|
||||
printf("add(multiply(3, 4), subtract(10, 5)) = %d\n", result);
|
||||
printf("PASS: Included functions in expressions work\n");
|
||||
|
||||
printf("\n=== All Include Tests Completed ===\n");
|
||||
return 0;
|
||||
}
|
||||
18
tests/includes/math_lib.rc
Normal file
18
tests/includes/math_lib.rc
Normal file
@ -0,0 +1,18 @@
|
||||
int add(int a, int b) {
|
||||
return a + b;
|
||||
}
|
||||
|
||||
int subtract(int a, int b) {
|
||||
return a - b;
|
||||
}
|
||||
|
||||
int multiply(int a, int b) {
|
||||
return a * b;
|
||||
}
|
||||
|
||||
int divide(int a, int b) {
|
||||
if (b == 0) {
|
||||
return 0;
|
||||
}
|
||||
return a / b;
|
||||
}
|
||||
19
tests/includes/string_lib.rc
Normal file
19
tests/includes/string_lib.rc
Normal file
@ -0,0 +1,19 @@
|
||||
int string_length(char *s) {
|
||||
return strlen(s);
|
||||
}
|
||||
|
||||
char* string_upper(char *s) {
|
||||
return upper(s);
|
||||
}
|
||||
|
||||
char* string_lower(char *s) {
|
||||
return lower(s);
|
||||
}
|
||||
|
||||
int string_contains(char *haystack, char *needle) {
|
||||
int pos = strpos(haystack, needle);
|
||||
if (pos >= 0) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
14
tests/includes/utils.rc
Normal file
14
tests/includes/utils.rc
Normal file
@ -0,0 +1,14 @@
|
||||
#include "math_lib.rc"
|
||||
#include "string_lib.rc"
|
||||
|
||||
int is_even(int n) {
|
||||
int half = divide(n, 2);
|
||||
return multiply(half, 2) == n;
|
||||
}
|
||||
|
||||
int is_odd(int n) {
|
||||
if (is_even(n)) {
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
22
tests/nested_include_test.rc
Normal file
22
tests/nested_include_test.rc
Normal file
@ -0,0 +1,22 @@
|
||||
#include "includes/utils.rc"
|
||||
|
||||
int main() {
|
||||
printf("=== Nested Include Tests ===\n");
|
||||
|
||||
printf("Test 1: Functions from nested includes\n");
|
||||
int sum = add(5, 3);
|
||||
printf("add(5, 3) = %d\n", sum);
|
||||
printf("PASS: Functions from first-level include work\n");
|
||||
|
||||
printf("Test 2: Utility functions using nested includes\n");
|
||||
if (is_even(10)) {
|
||||
printf("is_even(10) = true\n");
|
||||
}
|
||||
if (is_odd(7)) {
|
||||
printf("is_odd(7) = true\n");
|
||||
}
|
||||
printf("PASS: Utility functions using nested includes work\n");
|
||||
|
||||
printf("\n=== All Nested Include Tests Completed ===\n");
|
||||
return 0;
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user