/** * @file main.c * @brief Main entry point for Baba Yaga interpreter * @author eli_oat * @version 0.0.1 * @date 2025 * * This file contains the main entry point and command-line interface * for the Baba Yaga scripting language implementation. */ #include #include #include #include #include #include "baba_yaga.h" /* ============================================================================ * Constants * ============================================================================ */ #define VERSION "0.0.1" #define MAX_LINE_LENGTH 4096 #define MAX_FILE_SIZE (1024 * 1024) /* 1MB */ /* ============================================================================ * Function Declarations * ============================================================================ */ static void print_usage(const char* program_name); static void print_version(void); static void print_error(const char* message); static char* read_file(const char* filename); static void run_repl(Interpreter* interp); static void run_file(Interpreter* interp, const char* filename); static void run_tests(Interpreter* interp, const char* test_dir); /* ============================================================================ * Main Function * ============================================================================ */ /** * @brief Main entry point * * @param argc Argument count * @param argv Argument vector * @return Exit status */ int main(int argc, char* argv[]) { Interpreter* interp = NULL; int opt; bool run_repl_mode = false; (void)run_repl_mode; /* TODO: Use run_repl_mode variable */ bool run_test_mode = false; char* filename = NULL; char* test_dir = NULL; ExecResult result; Value value; /* Parse command line options */ while ((opt = getopt(argc, argv, "hvt:f:")) != -1) { switch (opt) { case 'h': print_usage(argv[0]); return EXIT_SUCCESS; case 'v': print_version(); return EXIT_SUCCESS; case 't': run_test_mode = true; test_dir = optarg; break; case 'f': filename = optarg; break; default: print_usage(argv[0]); return EXIT_FAILURE; } } /* Create interpreter */ interp = baba_yaga_create(); if (interp == NULL) { print_error("Failed to create interpreter"); return EXIT_FAILURE; } /* Set debug level from environment */ const char* debug_env = getenv("DEBUG"); if (debug_env != NULL) { int debug_level = atoi(debug_env); if (debug_level >= 0 && debug_level <= 5) { baba_yaga_set_debug_level((DebugLevel)debug_level); } } /* Execute based on mode */ if (run_test_mode) { run_tests(interp, test_dir); } else if (filename != NULL) { run_file(interp, filename); } else if (optind < argc) { /* Check if the argument looks like a file (not starting with -) */ char* arg = argv[optind]; if (arg[0] != '-' && access(arg, F_OK) == 0) { /* Treat as file */ run_file(interp, arg); } else { /* Execute source code from command line */ char* source = arg; value = baba_yaga_execute(interp, source, strlen(source), &result); if (result == EXEC_SUCCESS) { /* Print result using value_to_string for consistent formatting */ /* Don't print special IO return value */ if (value.type != VAL_NUMBER || value.data.number != -999999) { char* str = baba_yaga_value_to_string(&value); printf("%s\n", str); free(str); } } else { BabaYagaError* error = baba_yaga_get_error(interp); if (error != NULL) { fprintf(stderr, "Error: %s\n", error->message); baba_yaga_error_destroy(error); } else { fprintf(stderr, "Error: Execution failed\n"); } } baba_yaga_value_destroy(&value); } } else { run_repl(interp); } /* Cleanup */ baba_yaga_destroy(interp); return EXIT_SUCCESS; } /* ============================================================================ * Helper Functions * ============================================================================ */ /** * @brief Print usage information * * @param program_name Name of the program */ static void print_usage(const char* program_name) { printf("Baba Yaga C Implementation v%s\n", VERSION); printf("Usage: %s [OPTIONS] [SOURCE_CODE]\n", program_name); printf("\nOptions:\n"); printf(" -h, --help Show this help message\n"); printf(" -v, --version Show version information\n"); printf(" -f FILE Execute source code from file\n"); printf(" -t DIR Run tests from directory\n"); printf("\nExamples:\n"); printf(" %s # Start REPL\n", program_name); printf(" %s -f script.txt # Execute file\n", program_name); printf(" %s 'x : 42; ..out x' # Execute code\n", program_name); printf(" %s -t tests/ # Run tests\n", program_name); } /** * @brief Print version information */ static void print_version(void) { printf("Baba Yaga C Implementation v%s\n", VERSION); printf("Copyright (c) 2025 eli_oat\n"); printf("License: Custom - see LICENSE file\n"); } /** * @brief Print error message * * @param message Error message */ static void print_error(const char* message) { fprintf(stderr, "Error: %s\n", message); } /** * @brief Read entire file into memory * * @param filename Name of file to read * @return File contents (must be freed by caller) */ static char* read_file(const char* filename) { FILE* file; char* buffer; long file_size; size_t bytes_read; /* Open file */ file = fopen(filename, "rb"); if (file == NULL) { print_error("Failed to open file"); return NULL; } /* Get file size */ if (fseek(file, 0, SEEK_END) != 0) { fclose(file); print_error("Failed to seek to end of file"); return NULL; } file_size = ftell(file); if (file_size < 0) { fclose(file); print_error("Failed to get file size"); return NULL; } if (file_size > MAX_FILE_SIZE) { fclose(file); print_error("File too large"); return NULL; } /* Allocate buffer */ buffer = malloc(file_size + 1); if (buffer == NULL) { fclose(file); print_error("Failed to allocate memory"); return NULL; } /* Read file */ rewind(file); bytes_read = fread(buffer, 1, file_size, file); fclose(file); if (bytes_read != (size_t)file_size) { free(buffer); print_error("Failed to read file"); return NULL; } buffer[file_size] = '\0'; return buffer; } /** * @brief Run REPL (Read-Eval-Print Loop) * * @param interp Interpreter instance */ static void run_repl(Interpreter* interp) { char line[MAX_LINE_LENGTH]; ExecResult result; Value value; printf("Baba Yaga C Implementation v%s\n", VERSION); printf("Type 'exit' to quit\n\n"); while (1) { printf("baba-yaga> "); fflush(stdout); if (fgets(line, sizeof(line), stdin) == NULL) { break; } /* Remove newline */ line[strcspn(line, "\n")] = '\0'; /* Check for exit command */ if (strcmp(line, "exit") == 0) { break; } /* Skip empty lines */ if (strlen(line) == 0) { continue; } /* Execute line */ value = baba_yaga_execute(interp, line, 0, &result); if (result == EXEC_SUCCESS) { char* str = baba_yaga_value_to_string(&value); printf("%s\n", str); free(str); } else { BabaYagaError* error = baba_yaga_get_error(interp); if (error != NULL) { fprintf(stderr, "Error: %s\n", error->message); baba_yaga_error_destroy(error); } } baba_yaga_value_destroy(&value); } } /** * @brief Execute source code from file * * @param interp Interpreter instance * @param filename Name of file to execute */ static void run_file(Interpreter* interp, const char* filename) { char* source; ExecResult result; Value value; /* Read file */ source = read_file(filename); if (source == NULL) { return; } /* Execute source */ value = baba_yaga_execute(interp, source, strlen(source), &result); free(source); if (result == EXEC_SUCCESS) { /* Print result using value_to_string for consistent formatting */ /* Don't print special IO return value */ if (value.type != VAL_NUMBER || value.data.number != -999999) { char* str = baba_yaga_value_to_string(&value); printf("%s\n", str); free(str); } } else { BabaYagaError* error = baba_yaga_get_error(interp); if (error != NULL) { fprintf(stderr, "Error: %s\n", error->message); baba_yaga_error_destroy(error); } else { fprintf(stderr, "Error: Execution failed\n"); } exit(EXIT_FAILURE); } baba_yaga_value_destroy(&value); } /** * @brief Run tests from directory * * @param interp Interpreter instance * @param test_dir Test directory */ static void run_tests(Interpreter* interp, const char* test_dir) { (void)interp; /* TODO: Use interp parameter */ (void)test_dir; /* TODO: Use test_dir parameter */ /* TODO: Implement test runner */ printf("Test runner not yet implemented\n"); printf("Test directory: %s\n", test_dir); }