src/main.c
Rama main · réplica de lectura del repositorio original.
// SPDX-License-Identifier: MIT
#include "codegen/codegen.h"
#include "parser/parser.h"
#include "constants.h"
#include "tools/tool_common.h"
#if ZC_HAS_PLUGINS
#include "plugins/plugin_manager.h"
#endif
#include "zprep.h"
#include "analysis/typecheck.h"
#include "codegen/compat.h"
#include "driver/driver.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "utils/cmd.h"
#include "diagnostics/diagnostics.h"
#include <signal.h>
#if !defined(_WIN32)
#include <sys/wait.h>
#endif
// The standalone tools (zc-lsp, zc-repl, zc-doc, zc-format) ship alongside
// zc. `zc lsp|repl|doc|format` are deprecated wrappers that dispatch to them.
static int exec_tool(const char *cmd, const char *tool, int argc, char **argv)
{
fprintf(stderr, "note: 'zc %s' is deprecated; use '%s' directly\n", cmd, tool);
fflush(stderr);
// The tools are standalone binaries and do not expect the `zc` subcommand
// in argv, so forward everything after it.
char *forward[128];
int n = 0;
forward[n++] = NULL; // argv[0] placeholder (replaced with the tool path)
for (int i = 2; i < argc && n < 127; i++)
{
forward[n++] = argv[i];
}
forward[n] = NULL;
// z_get_executable_path returns the directory of the executable.
char self_path[MAX_PATH_SIZE];
z_get_executable_path(self_path, sizeof(self_path));
char path[MAX_PATH_SIZE + 32];
if (self_path[0])
{
snprintf(path, sizeof(path), "%s/%s", self_path, tool);
}
else
{
snprintf(path, sizeof(path), "%s", tool);
}
forward[0] = path;
execv(path, forward);
execvp(tool, forward);
fprintf(stderr, "error: could not launch '%s' (is it installed?)\n", tool);
return 127;
}
// Reports a --backend name that no registered backend has: the closest name, if one is
// within two edits, and the list of backends.
static void report_unknown_backend(const char *name)
{
int count = 0;
const CodegenBackend *const *backends = codegen_backends(&count);
const char *closest = NULL;
int best = 3;
for (int i = 0; i < count; i++)
{
int distance = levenshtein(name, backends[i]->name);
if (distance < best)
{
best = distance;
closest = backends[i]->name;
}
}
fprintf(stderr, "error: unknown backend '%s'\n", name);
if (closest)
{
fprintf(stderr, " did you mean '%s'?\n", closest);
}
fprintf(stderr, " available backends:");
for (int i = 0; i < count; i++)
{
fprintf(stderr, " %s", backends[i]->name);
}
fprintf(stderr, "\n");
}
int main(int argc, char **argv)
{
ZenCompiler compiler = {0};
CompilerConfig *cfg = &compiler.config;
z_compiler_setup(&compiler);
int i;
const char *optimization_level = NULL;
char *env_root;
char *input_file_copy;
i = 0;
(void)i;
(void)optimization_level;
(void)env_root;
(void)input_file_copy;
if (argc < 2)
{
print_usage();
return 1;
}
// Parse command
char *command = argv[1];
int arg_start = 2;
if (strcmp(command, "lsp") == 0)
{
return exec_tool("lsp", "zc-lsp", argc, argv);
}
else if (strcmp(command, "repl") == 0)
{
return exec_tool("repl", "zc-repl", argc, argv);
}
else if (strcmp(command, "doc") == 0)
{
return exec_tool("doc", "zc-doc", argc, argv);
}
else if (strcmp(command, "format") == 0)
{
return exec_tool("format", "zc-format", argc, argv);
}
else if (strcmp(command, "transpile") == 0 || strcmp(command, "-c") == 0)
{
cfg->mode_transpile = 1;
cfg->emit_c = 1; // Transpile implies emitting C
}
else if (strcmp(command, "run") == 0)
{
cfg->mode_run = 1;
}
else if (strcmp(command, "debug") == 0)
{
cfg->mode_debug = 1;
cfg->mode_run = 1;
}
else if (strcmp(command, "check") == 0)
{
cfg->mode_check = 1;
}
else if (strcmp(command, "help") == 0)
{
if (argc > 2)
{
print_command_help(argv[2]);
}
else
{
print_usage();
}
return 0;
}
else if (strcmp(command, "--help") == 0 || strcmp(command, "-h") == 0)
{
print_usage();
return 0;
}
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
cfg->input_file = command;
arg_start = 2; // already consumed
}
else
{
// Flags
arg_start = 1;
}
}
else if (strcmp(command, "build") == 0)
{
// Building is the default mode.
}
else if (strchr(command, '.'))
{
// "zc file.zc": build it
cfg->input_file = command;
arg_start = 2;
}
else
{
fprintf(stderr, "error: unknown command '%s'\n\n", command);
print_usage();
return 1;
}
// Parse args
for (i = arg_start; i < argc; i++)
{
char *arg = argv[i];
if (strcmp(arg, "--emit-c") == 0)
{
cfg->emit_c = 1;
}
else if (strcmp(arg, "--json") == 0)
{
compiler.diag_consumer = diag_render_json;
}
else if (strcmp(arg, "--keep-comments") == 0)
{
cfg->keep_comments = 1;
}
else if (strcmp(arg, "--recursive-doc") == 0 || strcmp(arg, "--no-recursive-doc") == 0)
{
fprintf(stderr, "note: --recursive-doc only applies to 'zc doc' (now 'zc-doc')\n");
}
else if (strcmp(arg, "--version") == 0 || strcmp(arg, "-V") == 0)
{
// Handled later
}
else if (strcmp(arg, "--paths") == 0)
{
// Handled later
}
else if (strcmp(arg, "--verbose") == 0 || strcmp(arg, "-v") == 0)
{
cfg->verbose = 1;
}
else if (strcmp(arg, "--quiet") == 0 || strcmp(arg, "-q") == 0)
{
cfg->quiet = 1;
}
else if (strcmp(arg, "--check") == 0 || strcmp(arg, "-c") == 0)
{
cfg->mode_check = 1;
cfg->use_typecheck = 1;
}
else if (strcmp(arg, "--no-check") == 0)
{
cfg->mode_check = 0;
cfg->use_typecheck = 0;
}
else if (strcmp(arg, "--profile") == 0)
{
if (i + 1 >= argc)
{
fprintf(stderr, "error: --profile requires a name (c11, hosted, misra-c2012)\n");
return 1;
}
const char *profile = argv[++i];
if (strcmp(profile, "c11") == 0 || strcmp(profile, "gnu") == 0)
{
cfg->profile = ZC_PROFILE_C11;
cfg->misra_mode = 0;
cfg->misra_output = 0;
}
else if (strcmp(profile, "hosted") == 0)
{
// MISRA-shaped codegen with a permissive source policy: the
// translation unit is allowed to use hosted APIs.
cfg->profile = ZC_PROFILE_HOSTED;
cfg->misra_mode = 0;
cfg->misra_output = 1;
}
else if (strcmp(profile, "misra-c2012") == 0)
{
cfg->profile = ZC_PROFILE_MISRA_C2012;
cfg->misra_mode = 1;
cfg->misra_output = 1;
cfg->use_typecheck = 1;
zvec_push_Str(&cfg->cfg_defines, xstrdup("misra"));
zvec_push_Str(&cfg->cfg_defines, xstrdup("ZC_MISRA"));
}
else
{
fprintf(stderr, "error: unknown profile '%s' (expected c11, hosted, misra-c2012)\n",
profile);
return 1;
}
}
else if (strcmp(arg, "--misra") == 0)
{
cfg->profile = ZC_PROFILE_MISRA_C2012;
cfg->misra_mode = 1;
cfg->misra_output = 1;
cfg->use_typecheck = 1;
zvec_push_Str(&cfg->cfg_defines, xstrdup("misra"));
zvec_push_Str(&cfg->cfg_defines, xstrdup("ZC_MISRA"));
}
else if (strcmp(arg, "--backend") == 0 && i + 1 < argc)
{
cfg->backend_name = argv[++i];
if (!codegen_find_backend(cfg->backend_name))
{
report_unknown_backend(cfg->backend_name);
return 1;
}
if (strcmp(cfg->backend_name, "cpp") == 0)
{
cfg->use_cpp = 1;
}
}
else if (strcmp(arg, "--backend-opt") == 0 && i + 1 < argc)
{
zvec_push_Str(&cfg->backend_opts, xstrdup(argv[++i]));
}
else if (strcmp(arg, "--freestanding") == 0)
{
cfg->is_freestanding = 1;
zvec_push_Str(&cfg->cfg_defines, xstrdup("freestanding"));
zvec_push_Str(&cfg->cfg_defines, xstrdup("ZC_FREESTANDING"));
}
else if (strcmp(arg, "--warn-errors") == 0)
{
cfg->warn_as_errors = 1;
}
else if (strcmp(arg, "--no-suppress-warnings") == 0)
{
cfg->no_suppress_warnings = 1;
}
else if (strcmp(arg, "--cpp") == 0)
{
if (z_is_windows())
{
strncpy(cfg->cc, "g++.exe", sizeof(cfg->cc) - 1);
cfg->cc[sizeof(cfg->cc) - 1] = '\0';
}
else
{
strncpy(cfg->cc, "g++", sizeof(cfg->cc) - 1);
cfg->cc[sizeof(cfg->cc) - 1] = '\0';
}
cfg->use_cpp = 1;
cfg->backend_name = "cpp";
}
else if (strcmp(arg, "--cuda") == 0)
{
if (z_is_windows())
{
strncpy(cfg->cc, "nvcc.exe", sizeof(cfg->cc) - 1);
cfg->cc[sizeof(cfg->cc) - 1] = '\0';
}
else
{
strncpy(cfg->cc, "nvcc", sizeof(cfg->cc) - 1);
cfg->cc[sizeof(cfg->cc) - 1] = '\0';
}
cfg->use_cuda = 1;
cfg->use_cpp = 1; // CUDA implies C++ mode.
cfg->backend_name = "cuda";
}
else if (strcmp(arg, "--objc") == 0 || strcmp(arg, "--objective-c") == 0)
{
cfg->use_objc = 1;
cfg->backend_name = "objc";
if (!cfg->cc[0] || strcmp(cfg->cc, "gcc") == 0)
{
if (z_is_windows())
{
snprintf(cfg->cc, sizeof(cfg->cc), "gcc");
}
else if (strcmp(z_get_system_name(), "macos") == 0)
{
snprintf(cfg->cc, sizeof(cfg->cc), "clang");
}
else
{
snprintf(cfg->cc, sizeof(cfg->cc), "gcc");
}
}
}
else if (strcmp(arg, "--filcc") == 0)
{
// Auto-discover bundled Fil-C compiler relative to known paths
const char *search_paths[] = {cfg->root_path, NULL};
int found = 0;
for (int pi = 0; search_paths[pi]; pi++)
{
if (!search_paths[pi] || !search_paths[pi][0])
{
continue;
}
char path[MAX_PATH_SIZE];
snprintf(path, sizeof(path), "%s/filc-0.678-linux-x86_64/build/bin/filcc",
search_paths[pi]);
if (access(path, X_OK) == 0)
{
size_t plen = strlen(path);
if (plen >= sizeof(cfg->cc))
{
plen = sizeof(cfg->cc) - 1;
}
memcpy(cfg->cc, path, (size_t)(plen));
cfg->cc[plen] = '\0';
char libpath[MAX_PATH_SIZE + 32];
snprintf(libpath, sizeof(libpath), "%s/filc-0.678-linux-x86_64/pizfix/lib",
search_paths[pi]);
#if ZC_OS_WINDOWS
SetEnvironmentVariableA("FILC_LIBRARY_PATH", libpath);
#else
setenv("FILC_LIBRARY_PATH", libpath, 1);
#endif
found = 1;
break;
}
}
if (!found)
{
snprintf(cfg->cc, sizeof(cfg->cc), "filcc");
}
}
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)
{
if (z_is_windows())
{
strncpy(cfg->cc, "zig.exe cc", sizeof(cfg->cc) - 1);
cfg->cc[sizeof(cfg->cc) - 1] = '\0';
}
else
{
strncpy(cfg->cc, "zig cc", sizeof(cfg->cc) - 1);
cfg->cc[sizeof(cfg->cc) - 1] = '\0';
}
}
else
{
snprintf(cfg->cc, sizeof(cfg->cc), "%s", cc_arg);
if (z_is_windows() && !z_path_has_extension(cfg->cc, ".exe"))
{
strcat(cfg->cc, ".exe");
}
}
}
}
else if (strncmp(arg, "-o", 2) == 0)
{
if (strlen(arg) > 2)
{
cfg->output_file = arg + 2;
}
else if (i + 1 < argc)
{
cfg->output_file = argv[++i];
}
else
{
fprintf(stderr, COLOR_BOLD COLOR_RED "error" COLOR_RESET
": missing output filename after '-o'\n");
return 1;
}
}
else if (strncmp(arg, "-I", 2) == 0)
{
char *i_path = NULL;
if (strlen(arg) > 2)
{
i_path = arg + 2;
}
else if (i + 1 < argc)
{
i_path = argv[++i];
}
if (i_path)
{
append_flag(cfg->gcc_flags, sizeof(cfg->gcc_flags), arg, NULL);
zvec_push_Str(&cfg->include_paths, xstrdup(i_path));
}
}
else if (strncmp(arg, "-L", 2) == 0 || strncmp(arg, "-l", 2) == 0)
{
char prefix[3] = {arg[0], arg[1], '\0'};
if (strlen(arg) > 2)
{
append_flag(compiler.link_flags, MAX_FLAGS_SIZE, prefix, arg + 2);
}
else if (i + 1 < argc)
{
append_flag(compiler.link_flags, MAX_FLAGS_SIZE, prefix, argv[++i]);
}
}
else if (strncmp(arg, "-O", 2) == 0)
{
if (strlen(arg) > 2)
{
optimization_level = arg + 2;
append_flag(cfg->gcc_flags, sizeof(cfg->gcc_flags), "-O", optimization_level);
}
else if (i + 1 < argc)
{
optimization_level = argv[++i];
append_flag(cfg->gcc_flags, sizeof(cfg->gcc_flags), "-O", optimization_level);
}
}
else if (strcmp(arg, "-g") == 0)
{
cfg->mode_debug = 1;
}
else if (strcmp(arg, "-g0") == 0 || strcmp(arg, "--no-debug") == 0)
{
cfg->mode_debug = 0;
}
else if (strcmp(arg, "--release") == 0)
{
cfg->mode_debug = 0;
append_flag(cfg->gcc_flags, sizeof(cfg->gcc_flags), "-O3", NULL);
}
else if (strncmp(arg, "-D", 2) == 0)
{
const char *def = (strlen(arg) > 2) ? arg + 2 : NULL;
if (!def && i + 1 < argc)
{
i++;
def = argv[i];
}
if (def)
{
char *name = xstrdup(def);
char *eq = (char *)strchr(name, '=');
if (eq)
{
*eq = '\0';
}
zvec_push_Str(&cfg->cfg_defines, name);
}
append_flag(cfg->gcc_flags, sizeof(cfg->gcc_flags), "-D", def);
}
else if (strncmp(arg, "-Wno-", 5) == 0)
{
if (!set_diag_by_name(cfg, arg + 5, 0))
{
append_flag(cfg->gcc_flags, sizeof(cfg->gcc_flags), arg, NULL);
}
}
else if (strncmp(arg, "-W", 2) == 0)
{
if (!set_diag_by_name(cfg, arg + 2, 1))
{
append_flag(cfg->gcc_flags, sizeof(cfg->gcc_flags), arg, NULL);
if (!cfg->warn_as_errors && strcmp(arg, "-Werror") == 0)
{
cfg->warn_as_errors = 1;
}
}
}
else if (strncmp(arg, "-f", 2) == 0 || strncmp(arg, "-m", 2) == 0 ||
strncmp(arg, "-x", 2) == 0 || strcmp(arg, "-S") == 0 || strcmp(arg, "-E") == 0 ||
strcmp(arg, "-shared") == 0 || strcmp(arg, "--shared") == 0)
{
// Standard C compiler flags that we want to pass directly to the backend
append_flag(cfg->gcc_flags, sizeof(cfg->gcc_flags), arg, NULL);
if (strcmp(arg, "-shared") == 0 || strcmp(arg, "--shared") == 0)
{
append_flag(cfg->gcc_flags, sizeof(cfg->gcc_flags), "-fPIC", NULL);
}
}
else if (arg[0] == '-')
{
// Check if this is a backend alias
const char *alias_opt = codegen_alias_lookup(arg);
if (alias_opt)
{
zvec_push_Str(&cfg->backend_opts, xstrdup(alias_opt));
}
else
{
// Unknown flag, pass to C compiler just in case
append_flag(cfg->gcc_flags, sizeof(cfg->gcc_flags), arg, NULL);
}
}
else if (arg[0] != '\0')
{
if (!cfg->input_file)
{
cfg->input_file = arg;
}
else
{
zvec_push_Str(&cfg->extra_files, arg);
}
}
}
for (i = arg_start; i < argc; i++)
{
char *arg = argv[i];
if (arg && (strcmp(arg, "--version") == 0 || strcmp(arg, "-V") == 0))
{
print_version();
return 0;
}
else if (strcmp(arg, "--paths") == 0)
{
print_search_paths(cfg);
return 0;
}
}
if (!cfg->input_file)
{
fprintf(stderr, COLOR_BOLD COLOR_RED "error" COLOR_RESET ": no input file specified\n");
return 1;
}
// Compute input directory
input_file_copy = xstrdup(cfg->input_file);
char *last_slash = z_path_last_sep(input_file_copy);
if (last_slash)
{
*last_slash = 0;
cfg->input_dir = xstrdup(input_file_copy);
}
else
{
cfg->input_dir = xstrdup(".");
}
zfree(input_file_copy);
return driver_run(&compiler);
}