diff options
| author | Zuhaitz Méndez Fernández de Aránguiz <zuhaitz@debian> | 2026-01-16 12:43:51 +0000 |
|---|---|---|
| committer | Zuhaitz Méndez Fernández de Aránguiz <zuhaitz@debian> | 2026-01-16 12:43:51 +0000 |
| commit | e77725b55190b8ec6dcab46aa137c32652ea004b (patch) | |
| tree | a80e237f1f65873f908f5819488c1c32683aff74 /src/main.c | |
| parent | 2e1aa3d8853f3b49e93b1d50b1b6e60e8238d79c (diff) | |
Support for 'alias' + test. Improved formatting and '.gitignore'.
Diffstat (limited to 'src/main.c')
| -rw-r--r-- | src/main.c | 540 |
1 files changed, 308 insertions, 232 deletions
@@ -12,258 +12,334 @@ // Forward decl for LSP int lsp_main(int argc, char **argv); -void print_search_paths() { - printf("Search paths:\n"); - printf(" ./\n"); - printf(" ./std/\n"); - printf(" /usr/local/share/zenc\n"); - printf(" /usr/share/zenc\n"); +void print_search_paths() +{ + printf("Search paths:\n"); + printf(" ./\n"); + printf(" ./std/\n"); + printf(" /usr/local/share/zenc\n"); + printf(" /usr/share/zenc\n"); } -void print_version() { printf("Zen C version %s\n", ZEN_VERSION); } - -void print_usage() { - printf("Usage: zc [command] [options] <file.zc>\n"); - printf("Commands:\n"); - printf(" run Compile and run the program\n"); - printf(" build Compile to executable\n"); - printf(" check Check for errors only\n"); - printf(" repl Start Interactive REPL\n"); - printf(" transpile Transpile to C code only (no compilation)\n"); - printf(" lsp Start Language Server\n"); - printf("Options:\n"); - printf(" --version Print version information"); - printf(" -o <file> Output executable name\n"); - printf(" --emit-c Keep generated C file (out.c)\n"); - printf(" --freestanding Freestanding mode (no stdlib)\n"); - printf(" --cc <compiler> C compiler to use (gcc, clang, tcc, zig)\n"); - printf(" -O<level> Optimization level\n"); - printf(" -g Debug info\n"); - printf(" -v, --verbose Verbose output\n"); - printf(" -q, --quiet Quiet output\n"); - printf(" -c Compile only (produce .o)\n"); +void print_version() +{ + printf("Zen C version %s\n", ZEN_VERSION); } -int main(int argc, char **argv) { - // Defaults - memset(&g_config, 0, sizeof(g_config)); - strcpy(g_config.cc, "gcc"); +void print_usage() +{ + printf("Usage: zc [command] [options] <file.zc>\n"); + printf("Commands:\n"); + printf(" run Compile and run the program\n"); + printf(" build Compile to executable\n"); + printf(" check Check for errors only\n"); + printf(" repl Start Interactive REPL\n"); + printf(" transpile Transpile to C code only (no compilation)\n"); + printf(" lsp Start Language Server\n"); + printf("Options:\n"); + printf(" --version Print version information"); + printf(" -o <file> Output executable name\n"); + printf(" --emit-c Keep generated C file (out.c)\n"); + printf(" --freestanding Freestanding mode (no stdlib)\n"); + printf(" --cc <compiler> C compiler to use (gcc, clang, tcc, zig)\n"); + printf(" -O<level> Optimization level\n"); + printf(" -g Debug info\n"); + printf(" -v, --verbose Verbose output\n"); + printf(" -q, --quiet Quiet output\n"); + printf(" -c Compile only (produce .o)\n"); +} - if (argc < 2) { - print_usage(); - return 1; - } +int main(int argc, char **argv) +{ + // Defaults + memset(&g_config, 0, sizeof(g_config)); + strcpy(g_config.cc, "gcc"); - // Parse command - char *command = argv[1]; - int arg_start = 2; + if (argc < 2) + { + print_usage(); + return 1; + } - if (strcmp(command, "lsp") == 0) { - return lsp_main(argc, argv); - } else if (strcmp(command, "repl") == 0) { - run_repl(argv[0]); // Pass self path for recursive calls - return 0; - } else if (strcmp(command, "transpile") == 0) { - g_config.mode_transpile = 1; - g_config.emit_c = 1; // Transpile implies emitting C - } else if (strcmp(command, "run") == 0) { - g_config.mode_run = 1; - } else if (strcmp(command, "check") == 0) { - g_config.mode_check = 1; - } else if (strcmp(command, "build") == 0) { - // default mode - } else if (command[0] == '-') { - // implicit build or run? assume build if starts with flag, but usually - // command first If file provided directly: "zc file.zc" -> build - if (strchr(command, '.')) { - // treat as filename - g_config.input_file = command; - arg_start = 2; // already consumed - } else { - // Flags - arg_start = 1; + // Parse command + char *command = argv[1]; + int arg_start = 2; + + if (strcmp(command, "lsp") == 0) + { + return lsp_main(argc, argv); + } + else if (strcmp(command, "repl") == 0) + { + run_repl(argv[0]); // Pass self path for recursive calls + return 0; + } + else if (strcmp(command, "transpile") == 0) + { + g_config.mode_transpile = 1; + g_config.emit_c = 1; // Transpile implies emitting C + } + else if (strcmp(command, "run") == 0) + { + g_config.mode_run = 1; } - } else { - // Check if file - if (strchr(command, '.')) { - g_config.input_file = command; - arg_start = 2; + else if (strcmp(command, "check") == 0) + { + g_config.mode_check = 1; } - } - - // Parse args - for (int i = arg_start; i < argc; i++) { - char *arg = argv[i]; - if (strcmp(arg, "--emit-c") == 0) { - g_config.emit_c = 1; - } else if (strcmp(arg, "--version") == 0 || strcmp(arg, "-V") == 0) { - print_version(); - return 0; - } else if (strcmp(arg, "--verbose") == 0 || strcmp(arg, "-v") == 0) { - g_config.verbose = 1; - } else if (strcmp(arg, "--quiet") == 0 || strcmp(arg, "-q") == 0) { - g_config.quiet = 1; - } else if (strcmp(arg, "--freestanding") == 0) { - g_config.is_freestanding = 1; - } else if (strcmp(arg, "--check") == 0) { - g_config.mode_check = 1; - } else if (strcmp(arg, "--cc") == 0) { - if (i + 1 < argc) { - char *cc_arg = argv[++i]; - // Handle "zig" shorthand for "zig cc" - if (strcmp(cc_arg, "zig") == 0) { - strcpy(g_config.cc, "zig cc"); - } else { - strcpy(g_config.cc, cc_arg); + else if (strcmp(command, "build") == 0) + { + // default mode + } + else if (command[0] == '-') + { + // implicit build or run? assume build if starts with flag, but usually + // command first If file provided directly: "zc file.zc" -> build + if (strchr(command, '.')) + { + // treat as filename + g_config.input_file = command; + arg_start = 2; // already consumed + } + else + { + // Flags + arg_start = 1; + } + } + else + { + // Check if file + if (strchr(command, '.')) + { + g_config.input_file = command; + arg_start = 2; + } + } + + // Parse args + for (int i = arg_start; i < argc; i++) + { + char *arg = argv[i]; + if (strcmp(arg, "--emit-c") == 0) + { + g_config.emit_c = 1; + } + else if (strcmp(arg, "--version") == 0 || strcmp(arg, "-V") == 0) + { + print_version(); + return 0; } - } - } else if (strcmp(arg, "-o") == 0) { - if (i + 1 < argc) { - g_config.output_file = argv[++i]; - } - } else if (strncmp(arg, "-O", 2) == 0) { - // Add to CFLAGS - strcat(g_config.gcc_flags, " "); - strcat(g_config.gcc_flags, arg); - } else if (strcmp(arg, "-g") == 0) { - strcat(g_config.gcc_flags, " -g"); - } else if (arg[0] == '-') { - // Unknown flag or C flag - strcat(g_config.gcc_flags, " "); - strcat(g_config.gcc_flags, arg); - } else { - if (!g_config.input_file) { - g_config.input_file = arg; - } else { - printf("Multiple input files not supported yet.\n"); + else if (strcmp(arg, "--verbose") == 0 || strcmp(arg, "-v") == 0) + { + g_config.verbose = 1; + } + else if (strcmp(arg, "--quiet") == 0 || strcmp(arg, "-q") == 0) + { + g_config.quiet = 1; + } + else if (strcmp(arg, "--freestanding") == 0) + { + g_config.is_freestanding = 1; + } + else if (strcmp(arg, "--check") == 0) + { + g_config.mode_check = 1; + } + else if (strcmp(arg, "--cc") == 0) + { + if (i + 1 < argc) + { + char *cc_arg = argv[++i]; + // Handle "zig" shorthand for "zig cc" + if (strcmp(cc_arg, "zig") == 0) + { + strcpy(g_config.cc, "zig cc"); + } + else + { + strcpy(g_config.cc, cc_arg); + } + } + } + else if (strcmp(arg, "-o") == 0) + { + if (i + 1 < argc) + { + g_config.output_file = argv[++i]; + } + } + else if (strncmp(arg, "-O", 2) == 0) + { + // Add to CFLAGS + strcat(g_config.gcc_flags, " "); + strcat(g_config.gcc_flags, arg); + } + else if (strcmp(arg, "-g") == 0) + { + strcat(g_config.gcc_flags, " -g"); + } + else if (arg[0] == '-') + { + // Unknown flag or C flag + strcat(g_config.gcc_flags, " "); + strcat(g_config.gcc_flags, arg); + } + else + { + if (!g_config.input_file) + { + g_config.input_file = arg; + } + else + { + printf("Multiple input files not supported yet.\n"); + return 1; + } + } + } + + if (!g_config.input_file) + { + printf("Error: No input file specified.\n"); return 1; - } } - } - - if (!g_config.input_file) { - printf("Error: No input file specified.\n"); - return 1; - } - - g_current_filename = g_config.input_file; - - // Load file - char *src = load_file(g_config.input_file); - if (!src) { - printf("Error: Could not read file %s\n", g_config.input_file); - return 1; - } - - init_builtins(); - zen_init(); - - // Initialize Plugin Manager - zptr_plugin_mgr_init(); - - // Parse context init - ParserContext ctx; - memset(&ctx, 0, sizeof(ctx)); - - // Scan for build directives (e.g. //> link: -lm) - scan_build_directives(&ctx, src); - - Lexer l; - lexer_init(&l, src); - - ctx.hoist_out = tmpfile(); // Temp file for plugin hoisting - if (!ctx.hoist_out) { - perror("tmpfile for hoisting"); - return 1; - } - g_parser_ctx = &ctx; - - if (!g_config.quiet) { - printf("[zc] Compiling %s...\n", g_config.input_file); - } - - ASTNode *root = parse_program(&ctx, &l); - if (!root) { - // Parse failed - return 1; - } - - if (g_config.mode_check) { - // Just verify - printf("Check passed.\n"); - return 0; - } - - // Codegen to C - FILE *out = fopen("out.c", "w"); - if (!out) { - perror("fopen out.c"); - return 1; - } - - codegen_node(&ctx, root, out); - fclose(out); - - if (g_config.mode_transpile) { - if (g_config.output_file) { - // If user specified -o, rename out.c to that - if (rename("out.c", g_config.output_file) != 0) { - perror("rename out.c"); + + g_current_filename = g_config.input_file; + + // Load file + char *src = load_file(g_config.input_file); + if (!src) + { + printf("Error: Could not read file %s\n", g_config.input_file); return 1; - } - if (!g_config.quiet) { - printf("[zc] Transpiled to %s\n", g_config.output_file); - } - } else { - if (!g_config.quiet) { - printf("[zc] Transpiled to out.c\n"); - } } - // Done, no C compilation - return 0; - } - // Compile C - char cmd[8192]; - char *outfile = g_config.output_file ? g_config.output_file : "a.out"; + init_builtins(); + zen_init(); - // TCC-specific adjustments? - // Already handled by user passing --cc tcc + // Initialize Plugin Manager + zptr_plugin_mgr_init(); - snprintf(cmd, sizeof(cmd), "%s %s %s %s %s -o %s out.c -lm %s -I./src %s", - g_config.cc, g_config.gcc_flags, g_cflags, - g_config.is_freestanding ? "-ffreestanding" : "", "", outfile, - g_parser_ctx->has_async ? "-lpthread" : "", g_link_flags); + // Parse context init + ParserContext ctx; + memset(&ctx, 0, sizeof(ctx)); - if (g_config.verbose) { - printf("[CMD] %s\n", cmd); - } + // Scan for build directives (e.g. //> link: -lm) + scan_build_directives(&ctx, src); - int ret = system(cmd); - if (ret != 0) { - printf("C compilation failed.\n"); - if (!g_config.emit_c) { - remove("out.c"); + Lexer l; + lexer_init(&l, src); + + ctx.hoist_out = tmpfile(); // Temp file for plugin hoisting + if (!ctx.hoist_out) + { + perror("tmpfile for hoisting"); + return 1; + } + g_parser_ctx = &ctx; + + if (!g_config.quiet) + { + printf("[zc] Compiling %s...\n", g_config.input_file); + } + + ASTNode *root = parse_program(&ctx, &l); + if (!root) + { + // Parse failed + return 1; + } + + if (g_config.mode_check) + { + // Just verify + printf("Check passed.\n"); + return 0; + } + + // Codegen to C + FILE *out = fopen("out.c", "w"); + if (!out) + { + perror("fopen out.c"); + return 1; + } + + codegen_node(&ctx, root, out); + fclose(out); + + if (g_config.mode_transpile) + { + if (g_config.output_file) + { + // If user specified -o, rename out.c to that + if (rename("out.c", g_config.output_file) != 0) + { + perror("rename out.c"); + return 1; + } + if (!g_config.quiet) + { + printf("[zc] Transpiled to %s\n", g_config.output_file); + } + } + else + { + if (!g_config.quiet) + { + printf("[zc] Transpiled to out.c\n"); + } + } + // Done, no C compilation + return 0; } - return 1; - } - - if (!g_config.emit_c) { - // remove("out.c"); // Keep it for debugging for now or follow flag - remove("out.c"); - } - - if (g_config.mode_run) { - char run_cmd[2048]; - sprintf(run_cmd, "./%s", outfile); - ret = system(run_cmd); - remove(outfile); + + // Compile C + char cmd[8192]; + char *outfile = g_config.output_file ? g_config.output_file : "a.out"; + + // TCC-specific adjustments? + // Already handled by user passing --cc tcc + + snprintf(cmd, sizeof(cmd), "%s %s %s %s %s -o %s out.c -lm %s -I./src %s", g_config.cc, + g_config.gcc_flags, g_cflags, g_config.is_freestanding ? "-ffreestanding" : "", "", + outfile, g_parser_ctx->has_async ? "-lpthread" : "", g_link_flags); + + if (g_config.verbose) + { + printf("[CMD] %s\n", cmd); + } + + int ret = system(cmd); + if (ret != 0) + { + printf("C compilation failed.\n"); + if (!g_config.emit_c) + { + remove("out.c"); + } + return 1; + } + + if (!g_config.emit_c) + { + // remove("out.c"); // Keep it for debugging for now or follow flag + remove("out.c"); + } + + if (g_config.mode_run) + { + char run_cmd[2048]; + sprintf(run_cmd, "./%s", outfile); + ret = system(run_cmd); + remove(outfile); + zptr_plugin_mgr_cleanup(); + zen_trigger_global(); + return ret; + } + zptr_plugin_mgr_cleanup(); zen_trigger_global(); - return ret; - } - - zptr_plugin_mgr_cleanup(); - zen_trigger_global(); - return 0; + return 0; } |
